From 4e72d945b634605ef37d7bb13dc282bef72aeb80 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 9 Oct 2015 14:00:35 -0700 Subject: [PATCH 001/232] try debuging the sh orientation on mac --- libraries/render-utils/src/DeferredGlobalLight.slh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 983b8002f7..6621f52124 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -125,7 +125,10 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi vec4 fragEyeVector = invViewMat * vec4(-position, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - vec3 color = diffuse.rgb * evalSphericalLight(ambientSphere, fragNormal).xyz * getLightAmbientIntensity(light); + vec3 mapVal = evalSkyboxLight(fragNormal, 0).xyz; + vec3 shVal = evalSphericalLight(ambientSphere, fragNormal).xyz; + vec3 ambientLight = (normal.x > 0 ? mapVal : shVal) * getLightAmbientIntensity(light); + vec3 color = diffuse.rgb * ambientLight; vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); From 1dfafec5afaf765fa7b44485efee9888aacbfc75 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Oct 2015 15:46:54 -0700 Subject: [PATCH 002/232] Prototyping controller refactoring --- libraries/controllers/CMakeLists.txt | 14 ++ .../src/controllers/ControllerMapping.cpp | 190 ++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 libraries/controllers/CMakeLists.txt create mode 100644 libraries/controllers/src/controllers/ControllerMapping.cpp diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt new file mode 100644 index 0000000000..fbabbe1463 --- /dev/null +++ b/libraries/controllers/CMakeLists.txt @@ -0,0 +1,14 @@ +set(TARGET_NAME controllers) + +# set a default root dir for each of our optional externals if it was not passed +setup_hifi_library(Script) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +link_hifi_libraries(shared plugins input-plugins) + +GroupSources("src/controllers") + +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + diff --git a/libraries/controllers/src/controllers/ControllerMapping.cpp b/libraries/controllers/src/controllers/ControllerMapping.cpp new file mode 100644 index 0000000000..59f8789d31 --- /dev/null +++ b/libraries/controllers/src/controllers/ControllerMapping.cpp @@ -0,0 +1,190 @@ +#include +#include + +#include +#include + +extern float currentTime(); + +namespace Controllers { + + /* + * Encapsulates a particular input / output, + * i.e. Hydra.Button0, Standard.X, Action.Yaw + */ + class Endpoint { + public: + virtual float value() = 0; + virtual void apply(float newValue, float oldValue, const Endpoint& source) = 0; + }; + + using EndpointList = std::list; + + const EndpointList& getHardwareEndpoints(); + + // Ex: xbox.RY, xbox.A .... + class HardwareEndpoint : public Endpoint { + public: + virtual float value() override { + // ... + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + // Default does nothing, but in theory this could be something like vibration + // mapping.from(xbox.X).to(xbox.Vibrate) + } + }; + + class VirtualEndpoint : public Endpoint { + public: + virtual void apply(float newValue) { + if (newValue != _lastValue) { + _lastValue = newValue; + } + } + + virtual float value() { + return _lastValue; + } + + float _lastValue; + }; + + /* + * A function which provides input + */ + class FunctionEndpoint : public Endpoint { + public: + + virtual float value() override { + float now = currentTime(); + float delta = now - _lastCalled; + float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); + _lastCalled = now; + return result; + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + if (newValue != oldValue) { + //_outputFunction.call(newValue, oldValue, source); + } + } + + float _lastValue{ NAN }; + float _lastCalled{ 0 }; + QScriptValue _outputFunction; + QScriptValue _inputFunction; + QScriptValue _object; + }; + + + // Encapsulates part of a filter chain + class Filter { + public: + virtual float apply(float newValue, float oldValue) = 0; + }; + + class ScaleFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + return newValue * _scale; + } + + float _scale{ 1.0 }; + }; + + class PulseFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + // ??? + } + + float _lastEmitValue{ 0 }; + float _lastEmitTime{ 0 }; + float _interval{ -1.0f }; + }; + + using FilterList = std::list; + + /* + * encapsulates a source, destination and filters to apply + */ + class Route { + public: + Endpoint* _source; + Endpoint* _destination; + FilterList _filters; + }; + + using ValueMap = std::map; + + class Mapping { + public: + // List of routes + using List = std::list; + // Map of source channels to route lists + using Map = std::map; + + Map _channelMappings; + ValueMap _lastValues; + }; + + class MappingsStack { + std::list _stack; + ValueMap _lastValues; + + void update() { + EndpointList hardwareInputs = getHardwareEndpoints(); + ValueMap currentValues; + + for (auto input : hardwareInputs) { + currentValues[input] = input->value(); + } + + // Now process the current values for each level of the stack + for (auto& mapping : _stack) { + update(mapping, currentValues); + } + + _lastValues = currentValues; + } + + void update(Mapping& mapping, ValueMap& values) { + ValueMap updates; + EndpointList consumedEndpoints; + for (const auto& entry : values) { + Endpoint* endpoint = entry.first; + if (!mapping._channelMappings.count(endpoint)) { + continue; + } + + const Mapping::List& routes = mapping._channelMappings[endpoint]; + consumedEndpoints.push_back(endpoint); + for (const auto& route : routes) { + float lastValue = 0; + if (mapping._lastValues.count(endpoint)) { + lastValue = mapping._lastValues[endpoint]; + } + float value = entry.second; + for (const auto& filter : route._filters) { + value = filter->apply(value, lastValue); + } + updates[route._destination] = value; + } + } + + // Update the last seen values + mapping._lastValues = values; + + // Remove all the consumed inputs + for (auto endpoint : consumedEndpoints) { + values.erase(endpoint); + } + + // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) + for (const auto& entry : updates) { + values[entry.first] = entry.second; + } + } + }; +} From f0af4f17753ae36a1a39109f9e03149de8abe066 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 9 Oct 2015 16:08:35 -0700 Subject: [PATCH 003/232] try debuging the sh orientation on mac --- libraries/render-utils/src/DeferredGlobalLight.slh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 6621f52124..351a178968 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -41,7 +41,7 @@ struct SphericalHarmonics { vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) { - vec3 dir = direction.xzy; // we don;t understand why yet but we need to use z as vertical axis? + vec3 dir = direction.xyz; // we don;t understand why yet but we need to use z as vertical axis? const float C1 = 0.429043; const float C2 = 0.511664; From 184e9a2209df5d4b6f7981d9bc76c41ca3b4ac88 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Oct 2015 16:20:55 -0700 Subject: [PATCH 004/232] Prototyping controllers --- interface/CMakeLists.txt | 3 +- .../src/controllers/ControllerMapping.h | 149 ++++++++++++++++++ .../controllers/src/controllers/Endpoint.cpp | 58 +++++++ .../controllers/src/controllers/Endpoint.h | 34 ++++ 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 libraries/controllers/src/controllers/ControllerMapping.h create mode 100644 libraries/controllers/src/controllers/Endpoint.cpp create mode 100644 libraries/controllers/src/controllers/Endpoint.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 63d1445496..2ce7f9a76d 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -118,7 +118,8 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) link_hifi_libraries(shared octree environment gpu procedural model render fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater - plugins display-plugins input-plugins) + plugins display-plugins input-plugins + controllers) add_dependency_external_projects(sdl2) diff --git a/libraries/controllers/src/controllers/ControllerMapping.h b/libraries/controllers/src/controllers/ControllerMapping.h new file mode 100644 index 0000000000..ade9984309 --- /dev/null +++ b/libraries/controllers/src/controllers/ControllerMapping.h @@ -0,0 +1,149 @@ +#include +#include + +#include +#include + +extern float currentTime(); + +namespace Controllers { + + + /* + * A function which provides input + */ + class FunctionEndpoint : public Endpoint { + public: + + virtual float value() override { + float now = currentTime(); + float delta = now - _lastCalled; + float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); + _lastCalled = now; + return result; + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + if (newValue != oldValue) { + //_outputFunction.call(newValue, oldValue, source); + } + } + + float _lastValue{ NAN }; + float _lastCalled{ 0 }; + QScriptValue _outputFunction; + QScriptValue _inputFunction; + QScriptValue _object; + }; + + + // Encapsulates part of a filter chain + class Filter { + public: + virtual float apply(float newValue, float oldValue) = 0; + }; + + class ScaleFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + return newValue * _scale; + } + + float _scale{ 1.0 }; + }; + + class PulseFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + // ??? + } + + float _lastEmitValue{ 0 }; + float _lastEmitTime{ 0 }; + float _interval{ -1.0f }; + }; + + using FilterList = std::list; + + /* + * encapsulates a source, destination and filters to apply + */ + class Route { + public: + Endpoint* _source; + Endpoint* _destination; + FilterList _filters; + }; + + using ValueMap = std::map; + + class Mapping { + public: + // List of routes + using List = std::list; + // Map of source channels to route lists + using Map = std::map; + + Map _channelMappings; + ValueMap _lastValues; + }; + + class MappingsStack { + std::list _stack; + ValueMap _lastValues; + + void update() { + EndpointList hardwareInputs = getHardwareEndpoints(); + ValueMap currentValues; + + for (auto input : hardwareInputs) { + currentValues[input] = input->value(); + } + + // Now process the current values for each level of the stack + for (auto& mapping : _stack) { + update(mapping, currentValues); + } + + _lastValues = currentValues; + } + + void update(Mapping& mapping, ValueMap& values) { + ValueMap updates; + EndpointList consumedEndpoints; + for (const auto& entry : values) { + Endpoint* endpoint = entry.first; + if (!mapping._channelMappings.count(endpoint)) { + continue; + } + + const Mapping::List& routes = mapping._channelMappings[endpoint]; + consumedEndpoints.push_back(endpoint); + for (const auto& route : routes) { + float lastValue = 0; + if (mapping._lastValues.count(endpoint)) { + lastValue = mapping._lastValues[endpoint]; + } + float value = entry.second; + for (const auto& filter : route._filters) { + value = filter->apply(value, lastValue); + } + updates[route._destination] = value; + } + } + + // Update the last seen values + mapping._lastValues = values; + + // Remove all the consumed inputs + for (auto endpoint : consumedEndpoints) { + values.erase(endpoint); + } + + // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) + for (const auto& entry : updates) { + values[entry.first] = entry.second; + } + } + }; +} diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/Endpoint.cpp new file mode 100644 index 0000000000..5f9e0b0efa --- /dev/null +++ b/libraries/controllers/src/controllers/Endpoint.cpp @@ -0,0 +1,58 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 +// + +#include "Endpoint.h" + +#include +#include + +namespace Controllers { + + // FIXME how do we handle dynamic changes in connected hardware? + const Endpoint::List& Endpoint::getHardwareEndpoints() { + static Endpoint::List ACTIVE_HARDWARE; + static std::once_flag once; + std::call_once(once, [&] { + auto userInputMapper = DependencyManager::get(); + // TODO populate ACTIVE_HARDWARE with all the connected devices + // For each connected device + // for each input channel + // build a HardwareEndpoint instance around the input channel and add it to the list + }); + } + + // Ex: xbox.RY, xbox.A .... + class HardwareEndpoint : public Endpoint { + public: + virtual float value() override { + // ... + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + // Default does nothing, but in theory this could be something like vibration + // mapping.from(xbox.X).to(xbox.Vibrate) + } + }; + + // Ex: Standard.RY, Action.Yaw + class VirtualEndpoint : public Endpoint { + public: + virtual void apply(float newValue) { + if (newValue != _lastValue) { + _lastValue = newValue; + } + } + + virtual float value() { + return _lastValue; + } + + float _lastValue; + }; + +} diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h new file mode 100644 index 0000000000..7361c15706 --- /dev/null +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -0,0 +1,34 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Endpoint_h +#define hifi_Controllers_Endpoint_h + +#include +#include + +namespace Controllers { + /* + * Encapsulates a particular input / output, + * i.e. Hydra.Button0, Standard.X, Action.Yaw + */ + class Endpoint { + public: + virtual float value() = 0; + virtual void apply(float newValue, float oldValue, const Endpoint& source) = 0; + + using Pointer = std::shared_ptr; + using List = std::list; + + static const List& getHardwareEndpoints(); + }; + +} + +#endif From c3775623aa6bd5084fee7e0fc86a68c04192d4b1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Oct 2015 17:23:52 -0700 Subject: [PATCH 005/232] Splitting files, adding test skeleton --- .../src/controllers/ControllerMapping.cpp | 190 ------------------ .../src/controllers/ControllerMapping.h | 149 -------------- .../controllers/src/controllers/Endpoint.cpp | 59 ++++-- .../controllers/src/controllers/Filter.cpp | 41 ++++ .../controllers/src/controllers/Filter.h | 29 +++ .../controllers/src/controllers/Mapping.cpp | 9 + .../controllers/src/controllers/Mapping.h | 87 ++++++++ libraries/controllers/src/controllers/Route.h | 32 +++ tests/controllers/CMakeLists.txt | 13 ++ tests/controllers/src/main.cpp | 38 ++++ 10 files changed, 295 insertions(+), 352 deletions(-) delete mode 100644 libraries/controllers/src/controllers/ControllerMapping.cpp delete mode 100644 libraries/controllers/src/controllers/ControllerMapping.h create mode 100644 libraries/controllers/src/controllers/Filter.cpp create mode 100644 libraries/controllers/src/controllers/Filter.h create mode 100644 libraries/controllers/src/controllers/Mapping.cpp create mode 100644 libraries/controllers/src/controllers/Mapping.h create mode 100644 libraries/controllers/src/controllers/Route.h create mode 100644 tests/controllers/CMakeLists.txt create mode 100644 tests/controllers/src/main.cpp diff --git a/libraries/controllers/src/controllers/ControllerMapping.cpp b/libraries/controllers/src/controllers/ControllerMapping.cpp deleted file mode 100644 index 59f8789d31..0000000000 --- a/libraries/controllers/src/controllers/ControllerMapping.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include -#include - -#include -#include - -extern float currentTime(); - -namespace Controllers { - - /* - * Encapsulates a particular input / output, - * i.e. Hydra.Button0, Standard.X, Action.Yaw - */ - class Endpoint { - public: - virtual float value() = 0; - virtual void apply(float newValue, float oldValue, const Endpoint& source) = 0; - }; - - using EndpointList = std::list; - - const EndpointList& getHardwareEndpoints(); - - // Ex: xbox.RY, xbox.A .... - class HardwareEndpoint : public Endpoint { - public: - virtual float value() override { - // ... - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - // Default does nothing, but in theory this could be something like vibration - // mapping.from(xbox.X).to(xbox.Vibrate) - } - }; - - class VirtualEndpoint : public Endpoint { - public: - virtual void apply(float newValue) { - if (newValue != _lastValue) { - _lastValue = newValue; - } - } - - virtual float value() { - return _lastValue; - } - - float _lastValue; - }; - - /* - * A function which provides input - */ - class FunctionEndpoint : public Endpoint { - public: - - virtual float value() override { - float now = currentTime(); - float delta = now - _lastCalled; - float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); - _lastCalled = now; - return result; - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - if (newValue != oldValue) { - //_outputFunction.call(newValue, oldValue, source); - } - } - - float _lastValue{ NAN }; - float _lastCalled{ 0 }; - QScriptValue _outputFunction; - QScriptValue _inputFunction; - QScriptValue _object; - }; - - - // Encapsulates part of a filter chain - class Filter { - public: - virtual float apply(float newValue, float oldValue) = 0; - }; - - class ScaleFilter : public Filter { - public: - virtual float apply(float newValue, float oldValue) { - return newValue * _scale; - } - - float _scale{ 1.0 }; - }; - - class PulseFilter : public Filter { - public: - virtual float apply(float newValue, float oldValue) { - // ??? - } - - float _lastEmitValue{ 0 }; - float _lastEmitTime{ 0 }; - float _interval{ -1.0f }; - }; - - using FilterList = std::list; - - /* - * encapsulates a source, destination and filters to apply - */ - class Route { - public: - Endpoint* _source; - Endpoint* _destination; - FilterList _filters; - }; - - using ValueMap = std::map; - - class Mapping { - public: - // List of routes - using List = std::list; - // Map of source channels to route lists - using Map = std::map; - - Map _channelMappings; - ValueMap _lastValues; - }; - - class MappingsStack { - std::list _stack; - ValueMap _lastValues; - - void update() { - EndpointList hardwareInputs = getHardwareEndpoints(); - ValueMap currentValues; - - for (auto input : hardwareInputs) { - currentValues[input] = input->value(); - } - - // Now process the current values for each level of the stack - for (auto& mapping : _stack) { - update(mapping, currentValues); - } - - _lastValues = currentValues; - } - - void update(Mapping& mapping, ValueMap& values) { - ValueMap updates; - EndpointList consumedEndpoints; - for (const auto& entry : values) { - Endpoint* endpoint = entry.first; - if (!mapping._channelMappings.count(endpoint)) { - continue; - } - - const Mapping::List& routes = mapping._channelMappings[endpoint]; - consumedEndpoints.push_back(endpoint); - for (const auto& route : routes) { - float lastValue = 0; - if (mapping._lastValues.count(endpoint)) { - lastValue = mapping._lastValues[endpoint]; - } - float value = entry.second; - for (const auto& filter : route._filters) { - value = filter->apply(value, lastValue); - } - updates[route._destination] = value; - } - } - - // Update the last seen values - mapping._lastValues = values; - - // Remove all the consumed inputs - for (auto endpoint : consumedEndpoints) { - values.erase(endpoint); - } - - // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) - for (const auto& entry : updates) { - values[entry.first] = entry.second; - } - } - }; -} diff --git a/libraries/controllers/src/controllers/ControllerMapping.h b/libraries/controllers/src/controllers/ControllerMapping.h deleted file mode 100644 index ade9984309..0000000000 --- a/libraries/controllers/src/controllers/ControllerMapping.h +++ /dev/null @@ -1,149 +0,0 @@ -#include -#include - -#include -#include - -extern float currentTime(); - -namespace Controllers { - - - /* - * A function which provides input - */ - class FunctionEndpoint : public Endpoint { - public: - - virtual float value() override { - float now = currentTime(); - float delta = now - _lastCalled; - float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); - _lastCalled = now; - return result; - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - if (newValue != oldValue) { - //_outputFunction.call(newValue, oldValue, source); - } - } - - float _lastValue{ NAN }; - float _lastCalled{ 0 }; - QScriptValue _outputFunction; - QScriptValue _inputFunction; - QScriptValue _object; - }; - - - // Encapsulates part of a filter chain - class Filter { - public: - virtual float apply(float newValue, float oldValue) = 0; - }; - - class ScaleFilter : public Filter { - public: - virtual float apply(float newValue, float oldValue) { - return newValue * _scale; - } - - float _scale{ 1.0 }; - }; - - class PulseFilter : public Filter { - public: - virtual float apply(float newValue, float oldValue) { - // ??? - } - - float _lastEmitValue{ 0 }; - float _lastEmitTime{ 0 }; - float _interval{ -1.0f }; - }; - - using FilterList = std::list; - - /* - * encapsulates a source, destination and filters to apply - */ - class Route { - public: - Endpoint* _source; - Endpoint* _destination; - FilterList _filters; - }; - - using ValueMap = std::map; - - class Mapping { - public: - // List of routes - using List = std::list; - // Map of source channels to route lists - using Map = std::map; - - Map _channelMappings; - ValueMap _lastValues; - }; - - class MappingsStack { - std::list _stack; - ValueMap _lastValues; - - void update() { - EndpointList hardwareInputs = getHardwareEndpoints(); - ValueMap currentValues; - - for (auto input : hardwareInputs) { - currentValues[input] = input->value(); - } - - // Now process the current values for each level of the stack - for (auto& mapping : _stack) { - update(mapping, currentValues); - } - - _lastValues = currentValues; - } - - void update(Mapping& mapping, ValueMap& values) { - ValueMap updates; - EndpointList consumedEndpoints; - for (const auto& entry : values) { - Endpoint* endpoint = entry.first; - if (!mapping._channelMappings.count(endpoint)) { - continue; - } - - const Mapping::List& routes = mapping._channelMappings[endpoint]; - consumedEndpoints.push_back(endpoint); - for (const auto& route : routes) { - float lastValue = 0; - if (mapping._lastValues.count(endpoint)) { - lastValue = mapping._lastValues[endpoint]; - } - float value = entry.second; - for (const auto& filter : route._filters) { - value = filter->apply(value, lastValue); - } - updates[route._destination] = value; - } - } - - // Update the last seen values - mapping._lastValues = values; - - // Remove all the consumed inputs - for (auto endpoint : consumedEndpoints) { - values.erase(endpoint); - } - - // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) - for (const auto& entry : updates) { - values[entry.first] = entry.second; - } - } - }; -} diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/Endpoint.cpp index 5f9e0b0efa..dddacc5ae5 100644 --- a/libraries/controllers/src/controllers/Endpoint.cpp +++ b/libraries/controllers/src/controllers/Endpoint.cpp @@ -13,19 +13,6 @@ namespace Controllers { - // FIXME how do we handle dynamic changes in connected hardware? - const Endpoint::List& Endpoint::getHardwareEndpoints() { - static Endpoint::List ACTIVE_HARDWARE; - static std::once_flag once; - std::call_once(once, [&] { - auto userInputMapper = DependencyManager::get(); - // TODO populate ACTIVE_HARDWARE with all the connected devices - // For each connected device - // for each input channel - // build a HardwareEndpoint instance around the input channel and add it to the list - }); - } - // Ex: xbox.RY, xbox.A .... class HardwareEndpoint : public Endpoint { public: @@ -55,4 +42,50 @@ namespace Controllers { float _lastValue; }; + float currentTime() { + return 0; + } + /* + * A function which provides input + */ + class FunctionEndpoint : public Endpoint { + public: + + virtual float value() override { + float now = currentTime(); + float delta = now - _lastCalled; + float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); + _lastCalled = now; + return result; + } + + virtual void apply(float newValue, float oldValue, const Endpoint& source) override { + if (newValue != oldValue) { + //_outputFunction.call(newValue, oldValue, source); + } + } + + float _lastValue{ NAN }; + float _lastCalled{ 0 }; + QScriptValue _outputFunction; + QScriptValue _inputFunction; + QScriptValue _object; + }; + + + + // FIXME how do we handle dynamic changes in connected hardware? + const Endpoint::List& Endpoint::getHardwareEndpoints() { + static Endpoint::List ACTIVE_HARDWARE_ENDPOINTS; + static std::once_flag once; + std::call_once(once, [&] { + auto userInputMapper = DependencyManager::get(); + // TODO populate ACTIVE_HARDWARE with all the connected devices + // For each connected device + // for each input channel + // build a HardwareEndpoint instance around the input channel and add it to the list + }); + + return ACTIVE_HARDWARE_ENDPOINTS; + } } diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp new file mode 100644 index 0000000000..93f5528073 --- /dev/null +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 +// + +#include "Filter.h" + +#include +#include + +namespace Controllers { + + class ScaleFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + return newValue * _scale; + } + + private: + float _scale{ 1.0 }; + }; + + class PulseFilter : public Filter { + public: + virtual float apply(float newValue, float oldValue) { + // ??? + } + + private: + + float _lastEmitValue{ 0 }; + float _lastEmitTime{ 0 }; + float _interval{ -1.0f }; + }; + + +} + diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h new file mode 100644 index 0000000000..9a0e4d975c --- /dev/null +++ b/libraries/controllers/src/controllers/Filter.h @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Filter_h +#define hifi_Controllers_Filter_h + +#include +#include + +namespace Controllers { + + // Encapsulates part of a filter chain + class Filter { + public: + virtual float apply(float newValue, float oldValue) = 0; + + using Pointer = std::shared_ptr; + using List = std::list; + }; + +} + +#endif diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp new file mode 100644 index 0000000000..dbcc6407f1 --- /dev/null +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -0,0 +1,9 @@ +#include "Mapping.h" + +#include +#include + +extern float currentTime(); + +namespace Controllers { +} diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h new file mode 100644 index 0000000000..46ed0f1a35 --- /dev/null +++ b/libraries/controllers/src/controllers/Mapping.h @@ -0,0 +1,87 @@ +#include +#include + +#include +#include + +extern float currentTime(); + +#include "Endpoint.h" +#include "Filter.h" +#include "Route.h" + +namespace Controllers { + + using ValueMap = std::map; + + class Mapping { + public: + // Map of source channels to route lists + using Map = std::map; + + Map _channelMappings; + ValueMap _lastValues; + + void parse(const QString& json); + QString serialize(); + }; + +// class MappingsStack { +// std::list _stack; +// ValueMap _lastValues; +// +// void update() { +// EndpointList hardwareInputs = getHardwareEndpoints(); +// ValueMap currentValues; +// +// for (auto input : hardwareInputs) { +// currentValues[input] = input->value(); +// } +// +// // Now process the current values for each level of the stack +// for (auto& mapping : _stack) { +// update(mapping, currentValues); +// } +// +// _lastValues = currentValues; +// } +// +// void update(Mapping& mapping, ValueMap& values) { +// ValueMap updates; +// EndpointList consumedEndpoints; +// for (const auto& entry : values) { +// Endpoint* endpoint = entry.first; +// if (!mapping._channelMappings.count(endpoint)) { +// continue; +// } +// +// const Mapping::List& routes = mapping._channelMappings[endpoint]; +// consumedEndpoints.push_back(endpoint); +// for (const auto& route : routes) { +// float lastValue = 0; +// if (mapping._lastValues.count(endpoint)) { +// lastValue = mapping._lastValues[endpoint]; +// } +// float value = entry.second; +// for (const auto& filter : route._filters) { +// value = filter->apply(value, lastValue); +// } +// updates[route._destination] = value; +// } +// } +// +// // Update the last seen values +// mapping._lastValues = values; +// +// // Remove all the consumed inputs +// for (auto endpoint : consumedEndpoints) { +// values.erase(endpoint); +// } +// +// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) +// for (const auto& entry : updates) { +// values[entry.first] = entry.second; +// } +// } +// }; +} diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h new file mode 100644 index 0000000000..616a5ebc23 --- /dev/null +++ b/libraries/controllers/src/controllers/Route.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Route_h +#define hifi_Controllers_Route_h + +#include "Endpoint.h" +#include "Filter.h" + +namespace Controllers { + + /* + * encapsulates a source, destination and filters to apply + */ + class Route { + public: + Endpoint::Pointer _source; + Endpoint::Pointer _destination; + Filter::List _filters; + + using Pointer = std::shared_ptr; + using List = std::list; + }; +} + +#endif diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt new file mode 100644 index 0000000000..4e31881141 --- /dev/null +++ b/tests/controllers/CMakeLists.txt @@ -0,0 +1,13 @@ + +set(TARGET_NAME controllers-test) + +AUTOSCRIBE_SHADER_LIB(gpu model render-utils ) + +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Script) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") + +# link in the shared libraries +link_hifi_libraries(shared script-engine input-plugins controllers) + +copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp new file mode 100644 index 0000000000..88d29214d3 --- /dev/null +++ b/tests/controllers/src/main.cpp @@ -0,0 +1,38 @@ +// +// main.cpp +// tests/gpu-test/src +// +// 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 +// + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + QWindow window; + app.exec(); + return 0; +} + +#include "main.moc" + From c02d33c17cebb798fbf7bff43b9579c9b6419a30 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 9 Oct 2015 17:27:50 -0700 Subject: [PATCH 006/232] Adding example mapping files --- .../resources/controllers/mapping-config.json | 24 +++++++++++++ .../resources/controllers/mapping-test0.json | 36 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 interface/resources/controllers/mapping-config.json create mode 100644 interface/resources/controllers/mapping-test0.json diff --git a/interface/resources/controllers/mapping-config.json b/interface/resources/controllers/mapping-config.json new file mode 100644 index 0000000000..053a637c6f --- /dev/null +++ b/interface/resources/controllers/mapping-config.json @@ -0,0 +1,24 @@ +{ + "name": "Full Mapping config including the standard hydra and gamepad and one more thing", + "mappings": [ + "src": "./mapping-hydra.json", + "src": "./mapping-xbox.json", + { + "name": "example mapping for standard to js function", + "channels": [ { + "from": "Standard.B", + "to": { + "type":"js", + "function": "function(value){ print("Standard.B = " + value );}" + } + }, { + "from": "Standard.B", + "to": { + "type":"js", + "src": "http://www.theNextBigThing.com/hifiInputSignalHandler.js" + } + } + ] + } + ] +} diff --git a/interface/resources/controllers/mapping-test0.json b/interface/resources/controllers/mapping-test0.json new file mode 100644 index 0000000000..c52b03be92 --- /dev/null +++ b/interface/resources/controllers/mapping-test0.json @@ -0,0 +1,36 @@ +{ + "name": "example mapping from Standard to actions", + "channels": [ { + "from": "Standard.LY", + "filters": [ { + "type": "clamp", + "min": 0, + "max": 1, + } + ], + "to": "Actions.Forward", + }, { + "from": "Standard.LY", + "filters": [ { + "type": "clamp", + "min": -1, + "max": 0, + }, { + "type": "invert" + } + ], + "to": "Actions.Backward", + }, { + "from": "Standard.LX", + "filters": [ { + "type": "scale", + "scale": 2.0, + } + ], + "to": "Actions.Yaw", + }, { + "from": "Standard.A", + "to": "Actions.Action0" + } + ] +} \ No newline at end of file From 78624679ed9f2bdcb17e7d96c52fd3b75545f29f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Oct 2015 17:30:46 -0700 Subject: [PATCH 007/232] Adding more parsing functions --- .../controllers/src/controllers/Filter.cpp | 4 +++ .../controllers/src/controllers/Filter.h | 4 +++ .../controllers/src/controllers/Mapping.cpp | 5 --- .../controllers/src/controllers/Mapping.h | 8 ++--- .../controllers/src/controllers/Route.cpp | 36 +++++++++++++++++++ libraries/controllers/src/controllers/Route.h | 4 +++ 6 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 libraries/controllers/src/controllers/Route.cpp diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index 93f5528073..6733ddc3e9 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -37,5 +37,9 @@ namespace Controllers { }; + Filter::Pointer Filter::parse(const QJsonObject& json) { + // FIXME parse the json object and determine the instance type to create + return Filter::Pointer(); + } } diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index 9a0e4d975c..a2921ee47d 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -13,6 +13,8 @@ #include #include +class QJsonObject; + namespace Controllers { // Encapsulates part of a filter chain @@ -22,6 +24,8 @@ namespace Controllers { using Pointer = std::shared_ptr; using List = std::list; + + static Filter::Pointer parse(const QJsonObject& json); }; } diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index dbcc6407f1..3fdbd71d1c 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -1,9 +1,4 @@ #include "Mapping.h" -#include -#include - -extern float currentTime(); - namespace Controllers { } diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 46ed0f1a35..f7e0899c15 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -1,15 +1,11 @@ #include -#include - -#include -#include - -extern float currentTime(); #include "Endpoint.h" #include "Filter.h" #include "Route.h" +class QString; + namespace Controllers { using ValueMap = std::map; diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/Route.cpp new file mode 100644 index 0000000000..29fb9b04eb --- /dev/null +++ b/libraries/controllers/src/controllers/Route.cpp @@ -0,0 +1,36 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Route_h +#define hifi_Controllers_Route_h + +#include "Endpoint.h" +#include "Filter.h" + +class QJsonObject; + +namespace Controllers { + + /* + * encapsulates a source, destination and filters to apply + */ + class Route { + public: + Endpoint::Pointer _source; + Endpoint::Pointer _destination; + Filter::List _filters; + + using Pointer = std::shared_ptr; + using List = std::list; + + void parse(const QJsonObject& json); + }; +} + +#endif diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h index 616a5ebc23..9459369d18 100644 --- a/libraries/controllers/src/controllers/Route.h +++ b/libraries/controllers/src/controllers/Route.h @@ -13,6 +13,8 @@ #include "Endpoint.h" #include "Filter.h" +class QJsonObject; + namespace Controllers { /* @@ -26,6 +28,8 @@ namespace Controllers { using Pointer = std::shared_ptr; using List = std::list; + + void parse(const QJsonObject); }; } From 297c3c9ed0dbacd7bcd3e9b82e6f98c69703451e Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 9 Oct 2015 17:37:51 -0700 Subject: [PATCH 008/232] Fixing json issues --- interface/resources/controllers/mapping-config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/mapping-config.json b/interface/resources/controllers/mapping-config.json index 053a637c6f..dd3bc7b05e 100644 --- a/interface/resources/controllers/mapping-config.json +++ b/interface/resources/controllers/mapping-config.json @@ -1,15 +1,15 @@ { "name": "Full Mapping config including the standard hydra and gamepad and one more thing", - "mappings": [ - "src": "./mapping-hydra.json", - "src": "./mapping-xbox.json", + "mappings": [ + { "src": "./mapping-hydra.json" }, + { "src": "./mapping-xbox.json" }, { "name": "example mapping for standard to js function", "channels": [ { "from": "Standard.B", "to": { "type":"js", - "function": "function(value){ print("Standard.B = " + value );}" + "function": "function(value){ print(\"Standard.B = \" + value );}" } }, { "from": "Standard.B", From 3d607f406f99a349504b32aedbfacef4d5ec296d Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 9 Oct 2015 17:40:36 -0700 Subject: [PATCH 009/232] revert change to the slf file --- libraries/render-utils/src/DeferredGlobalLight.slh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 351a178968..983b8002f7 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -41,7 +41,7 @@ struct SphericalHarmonics { vec4 evalSphericalLight(SphericalHarmonics sh, vec3 direction ) { - vec3 dir = direction.xyz; // we don;t understand why yet but we need to use z as vertical axis? + vec3 dir = direction.xzy; // we don;t understand why yet but we need to use z as vertical axis? const float C1 = 0.429043; const float C2 = 0.511664; @@ -125,10 +125,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi vec4 fragEyeVector = invViewMat * vec4(-position, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - vec3 mapVal = evalSkyboxLight(fragNormal, 0).xyz; - vec3 shVal = evalSphericalLight(ambientSphere, fragNormal).xyz; - vec3 ambientLight = (normal.x > 0 ? mapVal : shVal) * getLightAmbientIntensity(light); - vec3 color = diffuse.rgb * ambientLight; + vec3 color = diffuse.rgb * evalSphericalLight(ambientSphere, fragNormal).xyz * getLightAmbientIntensity(light); vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss); From c2da6600f54d07ebc712e73a50c727ce3d8e2cae Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Oct 2015 17:49:24 -0700 Subject: [PATCH 010/232] Fixing includes --- libraries/controllers/src/controllers/Mapping.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index f7e0899c15..6f843a7b23 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -1,11 +1,11 @@ #include +#include + #include "Endpoint.h" #include "Filter.h" #include "Route.h" -class QString; - namespace Controllers { using ValueMap = std::map; From 9e4a7a622637a8417776832a50305ab4b48b02b4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 10 Oct 2015 03:05:42 -0700 Subject: [PATCH 011/232] Working on controller refactoring --- .../controllers/src/controllers/Endpoint.h | 7 +- .../controllers/src/controllers/Filter.cpp | 24 --- .../controllers/src/controllers/Filter.h | 147 +++++++++++++++++- .../controllers/src/controllers/Mapping.cpp | 59 +++++++ .../controllers/src/controllers/Mapping.h | 74 ++------- .../NewControllerScriptingInterface.cpp | 125 +++++++++++++++ .../NewControllerScriptingInterface.h | 31 ++++ .../controllers/impl/MappingBuilderProxy.cpp | 33 ++++ .../controllers/impl/MappingBuilderProxy.h | 35 +++++ .../controllers/impl/RouteBuilderProxy.cpp | 80 ++++++++++ .../src/controllers/impl/RouteBuilderProxy.h | 42 +++++ libraries/shared/src/SharedUtil.cpp | 31 ++-- libraries/shared/src/SharedUtil.h | 5 + tests/controllers/qml/main.qml | 43 +++++ tests/controllers/src/main.cpp | 63 +++++++- 15 files changed, 701 insertions(+), 98 deletions(-) create mode 100644 libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp create mode 100644 libraries/controllers/src/controllers/NewControllerScriptingInterface.h create mode 100644 libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp create mode 100644 libraries/controllers/src/controllers/impl/MappingBuilderProxy.h create mode 100644 libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp create mode 100644 libraries/controllers/src/controllers/impl/RouteBuilderProxy.h create mode 100644 tests/controllers/qml/main.qml diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 7361c15706..48cdf015fa 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -13,6 +13,8 @@ #include #include +class QScriptValue; + namespace Controllers { /* * Encapsulates a particular input / output, @@ -20,13 +22,14 @@ namespace Controllers { */ class Endpoint { public: - virtual float value() = 0; - virtual void apply(float newValue, float oldValue, const Endpoint& source) = 0; + virtual float value() { return 0; } // = 0; + virtual void apply(float newValue, float oldValue, const Endpoint& source) {} // = 0; using Pointer = std::shared_ptr; using List = std::list; static const List& getHardwareEndpoints(); + static Pointer getEndpoint(const QScriptValue& value); }; } diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index 6733ddc3e9..e0c6adfcac 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -13,30 +13,6 @@ namespace Controllers { - class ScaleFilter : public Filter { - public: - virtual float apply(float newValue, float oldValue) { - return newValue * _scale; - } - - private: - float _scale{ 1.0 }; - }; - - class PulseFilter : public Filter { - public: - virtual float apply(float newValue, float oldValue) { - // ??? - } - - private: - - float _lastEmitValue{ 0 }; - float _lastEmitTime{ 0 }; - float _interval{ -1.0f }; - }; - - Filter::Pointer Filter::parse(const QJsonObject& json) { // FIXME parse the json object and determine the instance type to create return Filter::Pointer(); diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index a2921ee47d..de58dc3647 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -12,6 +12,10 @@ #include #include +#include +#include + +#include class QJsonObject; @@ -20,14 +24,155 @@ namespace Controllers { // Encapsulates part of a filter chain class Filter { public: - virtual float apply(float newValue, float oldValue) = 0; + virtual float apply(float value) const = 0; using Pointer = std::shared_ptr; using List = std::list; + using Lambda = std::function; static Filter::Pointer parse(const QJsonObject& json); }; + + class LambdaFilter : public Filter { + public: + LambdaFilter(Lambda f) : _function(f) {}; + + virtual float apply(float value) const { + return _function(value); + } + + private: + Lambda _function; + }; + + class ScriptFilter : public Filter { + public: + + }; + + //class ScaleFilter : public Filter { + //public: + // ScaleFilter(float scale); + // virtual float apply(float scale) const override { + // return value * _scale; + // } + + //private: + // const float _scale; + //}; + + //class AbstractRangeFilter : public Filter { + //public: + // RangeFilter(float min, float max) : _min(min), _max(max) {} + + //protected: + // const float _min; + // const float _max; + //}; + + ///* + //* Constrains will emit the input value on the first call, and every *interval* seconds, otherwise returns 0 + //*/ + //class PulseFilter : public Filter { + //public: + // PulseFilter(float interval); + // virtual float apply(float value) const override; + + //private: + // float _lastEmitTime{ -std::numeric_limits::max() }; + // const float _interval; + //}; + + ////class DeadzoneFilter : public AbstractRangeFilter { + ////public: + //// DeadzoneFilter(float min, float max = 1.0f); + //// virtual float apply(float newValue, float oldValue) override; + ////}; + + //class EasingFilter : public Filter { + //public: + // virtual float apply(float value) const override; + + //private: + // QEasingCurve _curve; + //}; + + //// GLSL style filters + //class StepFilter : public Filter { + //public: + // StepFilter(float edge) : _edge(edge) {}; + // virtual float apply(float value) const override; + + //private: + // const float _edge; + //}; + + //class PowFilter : public Filter { + //public: + // PowFilter(float exponent) : _exponent(exponent) {}; + // virtual float apply(float value) const override; + + //private: + // const float _exponent; + //}; + + //class ClampFilter : public RangeFilter { + //public: + // ClampFilter(float min = 0.0, float max = 1.0) : RangeFilter(min, max) {}; + // virtual float apply(float value) const override; + //}; + + //class AbsFilter : public Filter { + //public: + // virtual float apply(float value) const override; + //}; + + //class SignFilter : public Filter { + //public: + // virtual float apply(float value) const override; + //}; + + //class FloorFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return floor(newValue); + // } + //}; + + //class CeilFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return ceil(newValue); + // } + //}; + + //class FractFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return fract(newValue); + // } + //}; + + //class MinFilter : public Filter { + //public: + // MinFilter(float mine) : _min(min) {}; + + // virtual float apply(float value) const override { + // return glm::min(_min, newValue); + // } + + //private: + // const float _min; + //}; + + //class MaxFilter : public Filter { + //public: + // MaxFilter(float max) : _max(max) {}; + // virtual float apply(float newValue, float oldValue) override; + //private: + // const float _max; + //}; } #endif diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index 3fdbd71d1c..dd1ec14d1e 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -2,3 +2,62 @@ namespace Controllers { } + +// class MappingsStack { +// std::list _stack; +// ValueMap _lastValues; +// +// void update() { +// EndpointList hardwareInputs = getHardwareEndpoints(); +// ValueMap currentValues; +// +// for (auto input : hardwareInputs) { +// currentValues[input] = input->value(); +// } +// +// // Now process the current values for each level of the stack +// for (auto& mapping : _stack) { +// update(mapping, currentValues); +// } +// +// _lastValues = currentValues; +// } +// +// void update(Mapping& mapping, ValueMap& values) { +// ValueMap updates; +// EndpointList consumedEndpoints; +// for (const auto& entry : values) { +// Endpoint* endpoint = entry.first; +// if (!mapping._channelMappings.count(endpoint)) { +// continue; +// } +// +// const Mapping::List& routes = mapping._channelMappings[endpoint]; +// consumedEndpoints.push_back(endpoint); +// for (const auto& route : routes) { +// float lastValue = 0; +// if (mapping._lastValues.count(endpoint)) { +// lastValue = mapping._lastValues[endpoint]; +// } +// float value = entry.second; +// for (const auto& filter : route._filters) { +// value = filter->apply(value, lastValue); +// } +// updates[route._destination] = value; +// } +// } +// +// // Update the last seen values +// mapping._lastValues = values; +// +// // Remove all the consumed inputs +// for (auto endpoint : consumedEndpoints) { +// values.erase(endpoint); +// } +// +// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) +// for (const auto& entry : updates) { +// values[entry.first] = entry.second; +// } +// } +// }; diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 6f843a7b23..4154701478 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -1,3 +1,15 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Mapping_h +#define hifi_Controllers_Mapping_h + #include #include @@ -14,6 +26,8 @@ namespace Controllers { public: // Map of source channels to route lists using Map = std::map; + using Pointer = std::shared_ptr; + using List = std::list; Map _channelMappings; ValueMap _lastValues; @@ -22,62 +36,6 @@ namespace Controllers { QString serialize(); }; -// class MappingsStack { -// std::list _stack; -// ValueMap _lastValues; -// -// void update() { -// EndpointList hardwareInputs = getHardwareEndpoints(); -// ValueMap currentValues; -// -// for (auto input : hardwareInputs) { -// currentValues[input] = input->value(); -// } -// -// // Now process the current values for each level of the stack -// for (auto& mapping : _stack) { -// update(mapping, currentValues); -// } -// -// _lastValues = currentValues; -// } -// -// void update(Mapping& mapping, ValueMap& values) { -// ValueMap updates; -// EndpointList consumedEndpoints; -// for (const auto& entry : values) { -// Endpoint* endpoint = entry.first; -// if (!mapping._channelMappings.count(endpoint)) { -// continue; -// } -// -// const Mapping::List& routes = mapping._channelMappings[endpoint]; -// consumedEndpoints.push_back(endpoint); -// for (const auto& route : routes) { -// float lastValue = 0; -// if (mapping._lastValues.count(endpoint)) { -// lastValue = mapping._lastValues[endpoint]; -// } -// float value = entry.second; -// for (const auto& filter : route._filters) { -// value = filter->apply(value, lastValue); -// } -// updates[route._destination] = value; -// } -// } -// -// // Update the last seen values -// mapping._lastValues = values; -// -// // Remove all the consumed inputs -// for (auto endpoint : consumedEndpoints) { -// values.erase(endpoint); -// } -// -// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) -// for (const auto& entry : updates) { -// values[entry.first] = entry.second; -// } -// } -// }; } + +#endif \ No newline at end of file diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp new file mode 100644 index 0000000000..b8d5c851f5 --- /dev/null +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -0,0 +1,125 @@ +#include "NewControllerScriptingInterface.h" + +#include + +#include + +#include "GLMHelpers.h" + +#include "impl/MappingBuilderProxy.h" + +namespace Controllers { + +QObject* NewControllerScriptingInterface::newMapping() { + qDebug() << "Creating new Mapping proxy"; + return new MappingBuilderProxy(std::make_shared()); +} + +float NewControllerScriptingInterface::getValue(const QScriptValue& source) { + return 0; +} + +} // namespace controllers + + + + +// class MappingsStack { +// std::list _stack; +// ValueMap _lastValues; +// +// void update() { +// EndpointList hardwareInputs = getHardwareEndpoints(); +// ValueMap currentValues; +// +// for (auto input : hardwareInputs) { +// currentValues[input] = input->value(); +// } +// +// // Now process the current values for each level of the stack +// for (auto& mapping : _stack) { +// update(mapping, currentValues); +// } +// +// _lastValues = currentValues; +// } +// +// void update(Mapping& mapping, ValueMap& values) { +// ValueMap updates; +// EndpointList consumedEndpoints; +// for (const auto& entry : values) { +// Endpoint* endpoint = entry.first; +// if (!mapping._channelMappings.count(endpoint)) { +// continue; +// } +// +// const Mapping::List& routes = mapping._channelMappings[endpoint]; +// consumedEndpoints.push_back(endpoint); +// for (const auto& route : routes) { +// float lastValue = 0; +// if (mapping._lastValues.count(endpoint)) { +// lastValue = mapping._lastValues[endpoint]; +// } +// float value = entry.second; +// for (const auto& filter : route._filters) { +// value = filter->apply(value, lastValue); +// } +// updates[route._destination] = value; +// } +// } +// +// // Update the last seen values +// mapping._lastValues = values; +// +// // Remove all the consumed inputs +// for (auto endpoint : consumedEndpoints) { +// values.erase(endpoint); +// } +// +// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) +// for (const auto& entry : updates) { +// values[entry.first] = entry.second; +// } +// } +// }; +//var mapping = Controller.newMapping(); +//mapping.map(hydra.LeftButton0, actions.ContextMenu); +//mapping.map(hydra.LeftButton0).to(xbox.RT); +//mapping.from(xbox.RT).constrainToBoolean().invert().to(actions.Foo) +// mapping.from(xbox.RY).invert().deadZone(0.2).to(actions.Pitch) +// mapping.from(xbox.RY).filter(function(newValue, oldValue) { +// return newValue * 2.0 +//}).to(actions.Pitch) + +//mapping.from(function(time) { +// return Math.cos(time); +// }).to(actions.Pitch); + +// mapping.mapFromFunction(function() { +// return x; +// }, actions.ContextMenu); + +// mapping.from(xbox.LY).clamp(0, 1).to(actions.Forward); +// mapping.from(xbox.LY).clamp(-1, 0).to(actions.Backward); +// mapping.from(xbox.RY).clamp(0, 1).to(actions.Forward); +// mapping.from(xbox.RS).to(); +// mapping.from(xbox.ALL).to(); + +// mapping.from(xbox.RY).to(function(...) { ... }); +// mapping.from(xbox.RY).pass(); + +// mapping.suppress() ≅ mapping.to(null) +// mapping.pass() ≅ mapping.to(fromControl) + +// mapping.from(keyboard.RightParen).invert().to(actions.Yaw) +// mapping.from(keyboard.LeftParen).to(actions.Yaw) + +// mapping.from(hydra.LX).pulse(MIN_SNAP_TIME, 3.0).to(Actions.Yaw) + +// mapping.from(keyboard.LeftParen).pulse(MIN_SNAP_TIME).to(Actions.Yaw) +// // Enable and disable as above + +// mappingSnap.from(hydra.LX).to(function(newValue, oldValue) { +// timeSinceLastYaw += deltaTime + +#include "NewControllerScriptingInterface.moc" diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h new file mode 100644 index 0000000000..8730c2da89 --- /dev/null +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_NewControllerScriptingInterface_h +#define hifi_Controllers_NewControllerScriptingInterface_h + +#include +#include + +#include "Mapping.h" + +class QScriptValue; + +namespace Controllers { + class NewControllerScriptingInterface : public QObject { + Q_OBJECT + + public: + Q_INVOKABLE QObject* newMapping(); + Q_INVOKABLE float getValue(const QScriptValue& source); + }; +} + + +#endif diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp new file mode 100644 index 0000000000..e16fa511db --- /dev/null +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -0,0 +1,33 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 +// + +#include "MappingBuilderProxy.h" + +#include +#include + +#include "RouteBuilderProxy.h" + +namespace Controllers { + +QObject* MappingBuilderProxy::from(const QString& source) { + qDebug() << "Creating new Route builder proxy from " << source; + auto route = Route::Pointer(new Route()); + route->_source = endpointFor(source); + return new RouteBuilderProxy(this, route); +} + +Endpoint::Pointer MappingBuilderProxy::endpointFor(const QString& endpoint) { + static QHash ENDPOINTS; + if (!ENDPOINTS.contains(endpoint)) { + ENDPOINTS[endpoint] = std::make_shared(); + } + return ENDPOINTS[endpoint]; +} + +} diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h new file mode 100644 index 0000000000..6dac38b21e --- /dev/null +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -0,0 +1,35 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Impl_MappingBuilderProxy_h +#define hifi_Controllers_Impl_MappingBuilderProxy_h + +#include +#include + +#include "../Mapping.h" + +namespace Controllers { + +class MappingBuilderProxy : public QObject { + Q_OBJECT +public: + MappingBuilderProxy(Mapping::Pointer mapping) + : _mapping(mapping) { } + + Q_INVOKABLE QObject* from(const QString& fromEndpoint); + +protected: + friend class RouteBuilderProxy; + Endpoint::Pointer endpointFor(const QString& endpoint); + Mapping::Pointer _mapping; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp new file mode 100644 index 0000000000..d1659573e4 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -0,0 +1,80 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 +// +#include "RouteBuilderProxy.h" + +#include + +#include + +#include "MappingBuilderProxy.h" + +namespace Controllers { + +void RouteBuilderProxy::to(const QString& destination) { + qDebug() << "Completed route: " << destination; + auto sourceEndpoint = _route->_source; + auto& mapping = _parent->_mapping; + mapping->_channelMappings[sourceEndpoint].push_back(_route); + deleteLater(); +} + +QObject* RouteBuilderProxy::clamp(float min, float max) { + addFilter([=](float value) { + return glm::clamp(value, min, max); + }); + return this; +} + +QObject* RouteBuilderProxy::scale(float multiplier) { + addFilter([=](float value) { + return value * multiplier; + }); + return this; +} + +QObject* RouteBuilderProxy::invert() { + return scale(-1.0f); +} + +QObject* RouteBuilderProxy::deadZone(float min) { + assert(min < 1.0f); + float scale = 1.0f / (1.0f - min); + addFilter([=](float value) { + if (value < min) { + return 0.0f; + } + return (value - min) * scale; + }); + + return this; +} + +QObject* RouteBuilderProxy::constrainToInteger() { + addFilter([=](float value) { + return glm::sign(value); + }); + return this; +} + +QObject* RouteBuilderProxy::constrainToPositiveInteger() { + addFilter([=](float value) { + return (value <= 0.0f) ? 0.0f : 1.0f; + }); + return this; +} + +void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { + Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda); + addFilter(filterPointer); +} + +void RouteBuilderProxy::addFilter(Filter::Pointer filter) { + _route->_filters.push_back(filter); +} + +} diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h new file mode 100644 index 0000000000..516712b969 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -0,0 +1,42 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Impl_RouteBuilderProxy_h +#define hifi_Controllers_Impl_RouteBuilderProxy_h + +#include +#include "../Filter.h" +#include "../Route.h" + +namespace Controllers { + +class MappingBuilderProxy; + +class RouteBuilderProxy : public QObject { + Q_OBJECT + public: + RouteBuilderProxy(MappingBuilderProxy* parent, Route::Pointer route) + : _parent(parent), _route(route) { } + + Q_INVOKABLE void to(const QString& destination); + Q_INVOKABLE QObject* clamp(float min, float max); + Q_INVOKABLE QObject* scale(float multiplier); + Q_INVOKABLE QObject* invert(); + Q_INVOKABLE QObject* deadZone(float min); + Q_INVOKABLE QObject* constrainToInteger(); + Q_INVOKABLE QObject* constrainToPositiveInteger(); + + private: + void addFilter(Filter::Lambda lambda); + void addFilter(Filter::Pointer filter); + Route::Pointer _route; + MappingBuilderProxy* _parent; + }; + +} +#endif diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index f78c8c47e0..ccb6533dd7 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -39,30 +40,29 @@ void usecTimestampNowForceClockSkew(int clockSkew) { ::usecTimestampNowAdjust = clockSkew; } +static qint64 TIME_REFERENCE = 0; // in usec +static std::once_flag usecTimestampNowIsInitialized; +static QElapsedTimer timestampTimer; + quint64 usecTimestampNow(bool wantDebug) { - static bool usecTimestampNowIsInitialized = false; - static qint64 TIME_REFERENCE = 0; // in usec - static QElapsedTimer timestampTimer; - - if (!usecTimestampNowIsInitialized) { - TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec + std::call_once(usecTimestampNowIsInitialized, [&] { + TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec timestampTimer.start(); - usecTimestampNowIsInitialized = true; - } + }); quint64 now; quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); - quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec + quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it // will begin to deviate from real time. We detect that here, and reset if necessary quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch(); - quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / 1000; // usecs to msecs + quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs int possibleSkew = msecsEstimate - msecsCurrentTime; - const int TOLERANCE = 10000; // up to 10 seconds of skew is tolerated + const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated if (abs(possibleSkew) > TOLERANCE) { // reset our TIME_REFERENCE and timer - TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec + TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec timestampTimer.restart(); now = TIME_REFERENCE + ::usecTimestampNowAdjust; @@ -118,6 +118,13 @@ quint64 usecTimestampNow(bool wantDebug) { return now; } +float secTimestampNow() { + static const auto START_TIME = usecTimestampNow(); + const auto nowUsecs = usecTimestampNow(); + const auto nowMsecs = nowUsecs / USECS_PER_MSEC; + return (float)nowMsecs / MSECS_PER_SECOND; +} + float randFloat() { return (rand() % 10000)/10000.0f; } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 4967778cb4..98bc95cf6f 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -65,9 +65,14 @@ inline bool operator!=(const xColor& lhs, const xColor& rhs) // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)"; +// Equivalent to time_t but in usecs instead of secs quint64 usecTimestampNow(bool wantDebug = false); void usecTimestampNowForceClockSkew(int clockSkew); +// Number of seconds expressed since the first call to this function, expressed as a float +// Maximum accuracy in msecs +float ssecTimestampNow(); + float randFloat(); int randIntInRange (int min, int max); float randFloatInRange (float min,float max); diff --git a/tests/controllers/qml/main.qml b/tests/controllers/qml/main.qml new file mode 100644 index 0000000000..9f660e5d35 --- /dev/null +++ b/tests/controllers/qml/main.qml @@ -0,0 +1,43 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 +import com.highfidelity.test 1.0 + +ApplicationWindow { + id: window + visible: true + + AppHook { + + } + +// NewControllers { +// id: newControllers +// } + + Rectangle { + id: page + width: 320; height: 480 + color: "lightgray" + Text { + id: helloText + text: "Hello world!" + y: 30 + anchors.horizontalCenter: page.horizontalCenter + font.pointSize: 24; font.bold: true + } + + MouseArea { + anchors.fill: parent + onClicked: { + var newMapping = NewControllers.newMapping(); + console.log("Mapping Object " + newMapping); + var routeBuilder = newMapping.from("Hello"); + console.log("Route Builder " + routeBuilder); + routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye"); + } + } + } + +} diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 88d29214d3..8311b26dab 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -27,12 +27,73 @@ #include #include +#include +#include +#include + +#include + +const QString& getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/"; + qDebug() << "Qml Path: " << dir; + } + return dir; +} + +class AppHook : public QQuickItem { + Q_OBJECT + +public: + AppHook() { + qDebug() << "Hook Created"; + } +}; + +using namespace Controllers; + int main(int argc, char** argv) { + // Register our component type with QML. + qmlRegisterType("com.highfidelity.test", 1, 0, "AppHook"); + //qmlRegisterType("com.highfidelity.test", 1, 0, "NewControllers"); QGuiApplication app(argc, argv); - QWindow window; + QQmlApplicationEngine engine(getQmlDir() + "main.qml"); + engine.rootContext()->setContextProperty("NewControllers", new NewControllerScriptingInterface()); app.exec(); return 0; } + + +//QQmlEngine engine; +//QQmlComponent *component = new QQmlComponent(&engine); +// +//QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); +// +//component->loadUrl(QUrl("main.qml")); +// +//if (!component->isReady()) { +// qWarning("%s", qPrintable(component->errorString())); +// return -1; +//} +// +//QObject *topLevel = component->create(); +//QQuickWindow *window = qobject_cast(topLevel); +// +//QSurfaceFormat surfaceFormat = window->requestedFormat(); +//window->setFormat(surfaceFormat); +//window->show(); +// +//rc = app.exec(); +// +//delete component; +//return rc; +//} + + + #include "main.moc" From 80c95cd8eb79a5dacd32e2730ed7ac251d49f117 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 10 Oct 2015 16:07:26 -0700 Subject: [PATCH 012/232] Fixing access to second based timestamp --- libraries/shared/src/SharedUtil.cpp | 2 +- libraries/shared/src/SharedUtil.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index ccb6533dd7..145ec4ec37 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -120,7 +120,7 @@ quint64 usecTimestampNow(bool wantDebug) { float secTimestampNow() { static const auto START_TIME = usecTimestampNow(); - const auto nowUsecs = usecTimestampNow(); + const auto nowUsecs = usecTimestampNow() - START_TIME; const auto nowMsecs = nowUsecs / USECS_PER_MSEC; return (float)nowMsecs / MSECS_PER_SECOND; } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 98bc95cf6f..cd4f734d40 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -71,7 +71,7 @@ void usecTimestampNowForceClockSkew(int clockSkew); // Number of seconds expressed since the first call to this function, expressed as a float // Maximum accuracy in msecs -float ssecTimestampNow(); +float secTimestampNow(); float randFloat(); int randIntInRange (int min, int max); From 14f33258aecf22a82ac2fb673d52c0d6152da5de Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 10 Oct 2015 16:08:44 -0700 Subject: [PATCH 013/232] Working on test code, new controller interface --- .../NewControllerScriptingInterface.cpp | 33 +++-- .../NewControllerScriptingInterface.h | 3 +- tests/controllers/CMakeLists.txt | 12 +- tests/controllers/qml/content.qml | 137 ++++++++++++++++++ tests/controllers/qml/main.qml | 35 +---- tests/controllers/src/main.cpp | 71 ++++++++- 6 files changed, 243 insertions(+), 48 deletions(-) create mode 100644 tests/controllers/qml/content.qml diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index b8d5c851f5..bc915ba1a6 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -1,23 +1,36 @@ #include "NewControllerScriptingInterface.h" #include +#include -#include - -#include "GLMHelpers.h" +#include +#include +#include #include "impl/MappingBuilderProxy.h" namespace Controllers { + void NewControllerScriptingInterface::update() { + auto userInputMapper = DependencyManager::get(); + static float last = secTimestampNow(); + float now = secTimestampNow(); + userInputMapper->update(now - last); + last = now; + } -QObject* NewControllerScriptingInterface::newMapping() { - qDebug() << "Creating new Mapping proxy"; - return new MappingBuilderProxy(std::make_shared()); -} + QObject* NewControllerScriptingInterface::newMapping() { + qDebug() << "Creating new Mapping proxy"; + return new MappingBuilderProxy(std::make_shared()); + } -float NewControllerScriptingInterface::getValue(const QScriptValue& source) { - return 0; -} + float NewControllerScriptingInterface::getValue(const int& source) { + //UserInputMapper::Input input; input._id = source; + //auto userInputMapper = DependencyManager::get(); + //auto deviceProxy = userInputMapper->getDeviceProxy(input); + //return deviceProxy->getButton(input, 0) ? 1.0 : 0.0; + + return (sin(secTimestampNow()) + 1.0f) / 2.0f; + } } // namespace controllers diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index 8730c2da89..c4c42a2245 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -22,8 +22,9 @@ namespace Controllers { Q_OBJECT public: + Q_INVOKABLE void update(); Q_INVOKABLE QObject* newMapping(); - Q_INVOKABLE float getValue(const QScriptValue& source); + Q_INVOKABLE float getValue(const int& source); }; } diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index 4e31881141..34ab4c2eba 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -1,13 +1,19 @@ set(TARGET_NAME controllers-test) -AUTOSCRIBE_SHADER_LIB(gpu model render-utils ) - # This is not a testcase -- just set it up as a regular hifi project setup_hifi_project(Script) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared script-engine input-plugins controllers) +link_hifi_libraries(shared script-engine plugins input-plugins display-plugins controllers) + + +if (WIN32) + add_dependency_external_projects(OpenVR) + find_package(OpenVR REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) +endif() copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml new file mode 100644 index 0000000000..ac171ac42c --- /dev/null +++ b/tests/controllers/qml/content.qml @@ -0,0 +1,137 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Rectangle { + id: root + implicitHeight: column1.height + 24 + implicitWidth: column1.width + 24 + color: "lightgray" + + property real itemSize: 128 + + Component { + id: graphTemplate + Item { + implicitHeight: canvas.height + 2 + text.height + implicitWidth: canvas.width + property string text: loadText + + Canvas { + id: canvas + width: root.itemSize; height: root.itemSize; + antialiasing: false + property int controlId: control + property real value: 0.0 + property int drawWidth: 1 + + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + parent.value = NewControllers.getValue(canvas.controlId) + parent.requestPaint(); + } + } + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + + var image = ctx.getImageData(0, 0, canvas.width, canvas.height); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(image, -drawWidth, 0, canvas.width, canvas.height) + ctx.fillStyle = 'green' + // draw a filles rectangle + var height = canvas.height * canvas.value + ctx.fillRect(canvas.width - drawWidth, canvas.height - height, + drawWidth, height) + ctx.restore() + } + } + + Text { + id: text + text: parent.text + anchors.topMargin: 2 + anchors.horizontalCenter: canvas.horizontalCenter + anchors.top: canvas.bottom + font.pointSize: 12; + } + + } + } + + Column { + id: column1 + x: 12; y: 12 + spacing: 24 + Row { + spacing: 16 + Loader { + sourceComponent: graphTemplate; + property string loadText: "Key Left" + property int control: ControllerIds.Hardware.Keyboard2.Left + } + Loader { + sourceComponent: graphTemplate; + property string loadText: "DPad Up" + property int control: ControllerIds.Hardware.X360Controller1.DPadUp + } + /* + Loader { + sourceComponent: graphTemplate; + property string loadText: "Yaw Left" + property int control: ControllerIds.Actions.YAW_LEFT + } + Loader { + sourceComponent: graphTemplate; + property string loadText: "Yaw Left" + property int control: ControllerIds.Actions.YAW_LEFT + } +*/ + +// Loader { sourceComponent: graphTemplate; } +// Loader { sourceComponent: graphTemplate; } +// Loader { sourceComponent: graphTemplate; } + } + /* + Row { + spacing: 16 + Loader { sourceComponent: graphTemplate; } + Loader { sourceComponent: graphTemplate; } + Loader { sourceComponent: graphTemplate; } + Loader { sourceComponent: graphTemplate; } + } + Row { + spacing: 16 + Loader { sourceComponent: graphTemplate; } + Loader { sourceComponent: graphTemplate; } + Loader { sourceComponent: graphTemplate; } + Loader { sourceComponent: graphTemplate; } + } + */ + + + Button { + text: "Go!" + onClicked: { + // + +// var newMapping = NewControllers.newMapping(); +// console.log("Mapping Object " + newMapping); +// var routeBuilder = newMapping.from("Hello"); +// console.log("Route Builder " + routeBuilder); +// routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye"); + } + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + NewControllers.update(); + } + } + + } +} diff --git a/tests/controllers/qml/main.qml b/tests/controllers/qml/main.qml index 9f660e5d35..5ed68cc1fb 100644 --- a/tests/controllers/qml/main.qml +++ b/tests/controllers/qml/main.qml @@ -2,42 +2,13 @@ import QtQuick 2.1 import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import com.highfidelity.test 1.0 ApplicationWindow { id: window visible: true - AppHook { - + Loader { + id: pageLoader + source: "content.qml" } - -// NewControllers { -// id: newControllers -// } - - Rectangle { - id: page - width: 320; height: 480 - color: "lightgray" - Text { - id: helloText - text: "Hello world!" - y: 30 - anchors.horizontalCenter: page.horizontalCenter - font.pointSize: 24; font.bold: true - } - - MouseArea { - anchors.fill: parent - onClicked: { - var newMapping = NewControllers.newMapping(); - console.log("Mapping Object " + newMapping); - var routeBuilder = newMapping.from("Hello"); - console.log("Route Builder " + routeBuilder); - routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye"); - } - } - } - } diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 8311b26dab..9840038521 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -21,6 +23,7 @@ #include #include #include +#include #include #include @@ -32,6 +35,11 @@ #include #include +#include +#include +#include +#include +#include const QString& getQmlDir() { static QString dir; @@ -55,13 +63,72 @@ public: using namespace Controllers; -int main(int argc, char** argv) { + +QString sanatizeName(const QString& name) { + QString cleanName{ name }; + cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" }); + return cleanName; +} + +const QVariantMap& getInputMap() { + static std::once_flag once; + static QVariantMap map; + std::call_once(once, [&] { + { + QVariantMap hardwareMap; + // Controller.Hardware.* + auto devices = DependencyManager::get()->getDevices(); + for (const auto& deviceMapping : devices) { + auto device = deviceMapping.second.get(); + auto deviceName = sanatizeName(device->getName()); + auto deviceInputs = device->getAvailabeInputs(); + QVariantMap deviceMap; + for (const auto& inputMapping : deviceInputs) { + auto input = inputMapping.first; + auto inputName = sanatizeName(inputMapping.second); + deviceMap.insert(inputName, input.getID()); + } + hardwareMap.insert(deviceName, deviceMap); + } + map.insert("Hardware", hardwareMap); + } + + // Controller.Actions.* + { + QVariantMap actionMap; + auto actionNames = DependencyManager::get()->getActionNames(); + int actionNumber = 0; + for (const auto& actionName : actionNames) { + actionMap.insert(sanatizeName(actionName), actionNumber++); + } + map.insert("Actions", actionMap); + } + }); + return map; +} + +int main(int argc, char** argv) { + DependencyManager::set(); + PluginManager::getInstance()->getInputPlugins(); + + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + QString name = inputPlugin->getName(); + auto userInputMapper = DependencyManager::get(); + if (name == KeyboardMouseDevice::NAME) { + auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky + keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); + keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper); + } + } + // Register our component type with QML. qmlRegisterType("com.highfidelity.test", 1, 0, "AppHook"); //qmlRegisterType("com.highfidelity.test", 1, 0, "NewControllers"); QGuiApplication app(argc, argv); - QQmlApplicationEngine engine(getQmlDir() + "main.qml"); + QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("NewControllers", new NewControllerScriptingInterface()); + engine.rootContext()->setContextProperty("ControllerIds", getInputMap()); + engine.load(getQmlDir() + "main.qml"); app.exec(); return 0; } From 14f511350d5c3b88408cd48f710188ab2842a8a7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 10 Oct 2015 22:34:11 -0700 Subject: [PATCH 014/232] Working on refactoring the xbox hardware access and wiring up test code --- .../controllers/src/controllers/Endpoint.cpp | 80 +- .../controllers/src/controllers/Endpoint.h | 31 +- .../controllers/src/controllers/Filter.cpp | 2 +- .../controllers/src/controllers/Filter.h | 2 +- .../controllers/src/controllers/Logging.cpp | 11 + .../controllers/src/controllers/Logging.h | 16 + .../controllers/src/controllers/Mapping.cpp | 60 +- .../controllers/src/controllers/Mapping.h | 9 +- .../NewControllerScriptingInterface.cpp | 386 ++++++++-- .../NewControllerScriptingInterface.h | 65 +- .../controllers/src/controllers/Route.cpp | 3 +- libraries/controllers/src/controllers/Route.h | 2 +- .../controllers/impl/MappingBuilderProxy.cpp | 33 +- .../controllers/impl/MappingBuilderProxy.h | 20 +- .../controllers/impl/RouteBuilderProxy.cpp | 76 +- .../src/controllers/impl/RouteBuilderProxy.h | 24 +- .../src/input-plugins/InputPlugin.cpp | 4 +- .../src/input-plugins/Joystick.cpp | 176 ++--- .../src/input-plugins/Joystick.h | 19 +- .../src/input-plugins/KeyboardMouseDevice.cpp | 7 +- .../src/input-plugins/KeyboardMouseDevice.h | 7 +- .../src/input-plugins/StandardController.cpp | 157 ++-- .../src/input-plugins/StandardController.h | 39 +- .../src/input-plugins/StandardControls.h | 55 ++ .../src/input-plugins/UserInputMapper.cpp | 703 +++++++++--------- .../src/input-plugins/UserInputMapper.h | 20 +- tests/controllers/qml/Xbox.qml | 99 +++ tests/controllers/qml/content.qml | 234 +++--- .../controllers/qml/controls/AnalogButton.qml | 45 ++ .../controllers/qml/controls/AnalogStick.qml | 50 ++ .../qml/controls/ScrollingGraph.qml | 104 +++ .../controllers/qml/controls/ToggleButton.qml | 43 ++ tests/controllers/qml/xbox/DPad.qml | 43 ++ .../controllers/qml/xbox/LeftAnalogStick.qml | 21 + .../controllers/qml/xbox/RightAnalogStick.qml | 21 + tests/controllers/qml/xbox/XboxButtons.qml | 46 ++ .../qml/xbox/xbox360-controller-md.png | Bin 0 -> 29588 bytes tests/controllers/src/main.cpp | 274 +++---- 38 files changed, 1815 insertions(+), 1172 deletions(-) create mode 100644 libraries/controllers/src/controllers/Logging.cpp create mode 100644 libraries/controllers/src/controllers/Logging.h create mode 100644 libraries/input-plugins/src/input-plugins/StandardControls.h create mode 100644 tests/controllers/qml/Xbox.qml create mode 100644 tests/controllers/qml/controls/AnalogButton.qml create mode 100644 tests/controllers/qml/controls/AnalogStick.qml create mode 100644 tests/controllers/qml/controls/ScrollingGraph.qml create mode 100644 tests/controllers/qml/controls/ToggleButton.qml create mode 100644 tests/controllers/qml/xbox/DPad.qml create mode 100644 tests/controllers/qml/xbox/LeftAnalogStick.qml create mode 100644 tests/controllers/qml/xbox/RightAnalogStick.qml create mode 100644 tests/controllers/qml/xbox/XboxButtons.qml create mode 100644 tests/controllers/qml/xbox/xbox360-controller-md.png diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/Endpoint.cpp index dddacc5ae5..3f1d12b9de 100644 --- a/libraries/controllers/src/controllers/Endpoint.cpp +++ b/libraries/controllers/src/controllers/Endpoint.cpp @@ -8,84 +8,6 @@ #include "Endpoint.h" -#include -#include +namespace controller { -namespace Controllers { - - // Ex: xbox.RY, xbox.A .... - class HardwareEndpoint : public Endpoint { - public: - virtual float value() override { - // ... - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - // Default does nothing, but in theory this could be something like vibration - // mapping.from(xbox.X).to(xbox.Vibrate) - } - }; - - // Ex: Standard.RY, Action.Yaw - class VirtualEndpoint : public Endpoint { - public: - virtual void apply(float newValue) { - if (newValue != _lastValue) { - _lastValue = newValue; - } - } - - virtual float value() { - return _lastValue; - } - - float _lastValue; - }; - - float currentTime() { - return 0; - } - /* - * A function which provides input - */ - class FunctionEndpoint : public Endpoint { - public: - - virtual float value() override { - float now = currentTime(); - float delta = now - _lastCalled; - float result = _inputFunction.call(_object, QScriptValue(delta)).toNumber(); - _lastCalled = now; - return result; - } - - virtual void apply(float newValue, float oldValue, const Endpoint& source) override { - if (newValue != oldValue) { - //_outputFunction.call(newValue, oldValue, source); - } - } - - float _lastValue{ NAN }; - float _lastCalled{ 0 }; - QScriptValue _outputFunction; - QScriptValue _inputFunction; - QScriptValue _object; - }; - - - - // FIXME how do we handle dynamic changes in connected hardware? - const Endpoint::List& Endpoint::getHardwareEndpoints() { - static Endpoint::List ACTIVE_HARDWARE_ENDPOINTS; - static std::once_flag once; - std::call_once(once, [&] { - auto userInputMapper = DependencyManager::get(); - // TODO populate ACTIVE_HARDWARE with all the connected devices - // For each connected device - // for each input channel - // build a HardwareEndpoint instance around the input channel and add it to the list - }); - - return ACTIVE_HARDWARE_ENDPOINTS; - } } diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 48cdf015fa..bea33517f5 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -12,26 +12,45 @@ #include #include +#include + +#include class QScriptValue; -namespace Controllers { +namespace controller { /* * Encapsulates a particular input / output, * i.e. Hydra.Button0, Standard.X, Action.Yaw */ class Endpoint { public: - virtual float value() { return 0; } // = 0; - virtual void apply(float newValue, float oldValue, const Endpoint& source) {} // = 0; - using Pointer = std::shared_ptr; using List = std::list; + using Pair = std::pair; + using ReadLambda = std::function; + using WriteLambda = std::function; - static const List& getHardwareEndpoints(); - static Pointer getEndpoint(const QScriptValue& value); + Endpoint(const UserInputMapper::Input& id) : _id(id) {} + virtual float value() = 0; + virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; + const UserInputMapper::Input& getId() { return _id; } + protected: + UserInputMapper::Input _id; }; + class LambdaEndpoint : public Endpoint { + public: + LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {}) + : Endpoint(UserInputMapper::Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } + + virtual float value() override { return _readLambda(); } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { _writeLambda(newValue); } + + private: + ReadLambda _readLambda; + WriteLambda _writeLambda; + }; } #endif diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index e0c6adfcac..17715eceff 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -11,7 +11,7 @@ #include #include -namespace Controllers { +namespace controller { Filter::Pointer Filter::parse(const QJsonObject& json) { // FIXME parse the json object and determine the instance type to create diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index de58dc3647..f3978e2c0a 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -19,7 +19,7 @@ class QJsonObject; -namespace Controllers { +namespace controller { // Encapsulates part of a filter chain class Filter { diff --git a/libraries/controllers/src/controllers/Logging.cpp b/libraries/controllers/src/controllers/Logging.cpp new file mode 100644 index 0000000000..ae6b523a45 --- /dev/null +++ b/libraries/controllers/src/controllers/Logging.cpp @@ -0,0 +1,11 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// 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 +// + +#include "Logging.h" + +Q_LOGGING_CATEGORY(controllers, "hifi.controllers") diff --git a/libraries/controllers/src/controllers/Logging.h b/libraries/controllers/src/controllers/Logging.h new file mode 100644 index 0000000000..d74ddae59f --- /dev/null +++ b/libraries/controllers/src/controllers/Logging.h @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis 2015/10/11 +// 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 +// + +#ifndef hifi_Controllers_Logging_h +#define hifi_Controllers_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(controllers) + +#endif diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index dd1ec14d1e..0063a1d24a 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -1,63 +1,5 @@ #include "Mapping.h" -namespace Controllers { +namespace controller { } -// class MappingsStack { -// std::list _stack; -// ValueMap _lastValues; -// -// void update() { -// EndpointList hardwareInputs = getHardwareEndpoints(); -// ValueMap currentValues; -// -// for (auto input : hardwareInputs) { -// currentValues[input] = input->value(); -// } -// -// // Now process the current values for each level of the stack -// for (auto& mapping : _stack) { -// update(mapping, currentValues); -// } -// -// _lastValues = currentValues; -// } -// -// void update(Mapping& mapping, ValueMap& values) { -// ValueMap updates; -// EndpointList consumedEndpoints; -// for (const auto& entry : values) { -// Endpoint* endpoint = entry.first; -// if (!mapping._channelMappings.count(endpoint)) { -// continue; -// } -// -// const Mapping::List& routes = mapping._channelMappings[endpoint]; -// consumedEndpoints.push_back(endpoint); -// for (const auto& route : routes) { -// float lastValue = 0; -// if (mapping._lastValues.count(endpoint)) { -// lastValue = mapping._lastValues[endpoint]; -// } -// float value = entry.second; -// for (const auto& filter : route._filters) { -// value = filter->apply(value, lastValue); -// } -// updates[route._destination] = value; -// } -// } -// -// // Update the last seen values -// mapping._lastValues = values; -// -// // Remove all the consumed inputs -// for (auto endpoint : consumedEndpoints) { -// values.erase(endpoint); -// } -// -// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) -// for (const auto& entry : updates) { -// values[entry.first] = entry.second; -// } -// } -// }; diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 4154701478..5b54a1745b 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -11,6 +11,7 @@ #define hifi_Controllers_Mapping_h #include +#include #include @@ -18,19 +19,15 @@ #include "Filter.h" #include "Route.h" -namespace Controllers { - - using ValueMap = std::map; +namespace controller { class Mapping { public: // Map of source channels to route lists using Map = std::map; using Pointer = std::shared_ptr; - using List = std::list; Map _channelMappings; - ValueMap _lastValues; void parse(const QString& json); QString serialize(); @@ -38,4 +35,4 @@ namespace Controllers { } -#endif \ No newline at end of file +#endif diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index bc915ba1a6..f5d6276b91 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -1,100 +1,330 @@ -#include "NewControllerScriptingInterface.h" +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 +// +#include "NewControllerScriptingInterface.h" #include #include +#include + #include #include #include +#include +#include +#include #include "impl/MappingBuilderProxy.h" +#include "Logging.h" -namespace Controllers { - void NewControllerScriptingInterface::update() { - auto userInputMapper = DependencyManager::get(); - static float last = secTimestampNow(); - float now = secTimestampNow(); - userInputMapper->update(now - last); - last = now; +static const uint16_t ACTIONS_DEVICE = UserInputMapper::Input::INVALID_DEVICE - (uint16_t)1; + +namespace controller { + + + class VirtualEndpoint : public Endpoint { + public: + VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1)) + : Endpoint(id) { + } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } + + private: + float _currentValue{ 0.0f }; + }; + + + class JSEndpoint : public Endpoint { + public: + JSEndpoint(const QJSValue& callable) + : Endpoint(UserInputMapper::Input(-1)), _callable(callable) {} + + virtual float value() { + float result = (float)_callable.call().toNumber();; + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + _callable.call(QJSValueList({ QJSValue(newValue) })); + } + + private: + QJSValue _callable; + }; + + class CompositeEndpoint : public Endpoint, Endpoint::Pair { + public: + CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) + : Endpoint(UserInputMapper::Input(-1)), Pair(first, second) { } + + virtual float value() { + float result = first->value() * -1.0 + second->value(); + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + // Composites are read only + } + + private: + Endpoint::Pointer _first; + Endpoint::Pointer _second; + }; + + QString sanatizeName(const QString& name) { + QString cleanName{ name }; + cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" }); + return cleanName; } - QObject* NewControllerScriptingInterface::newMapping() { - qDebug() << "Creating new Mapping proxy"; - return new MappingBuilderProxy(std::make_shared()); + QVariantMap createDeviceMap(const UserInputMapper::DeviceProxy* device) { + auto userInputMapper = DependencyManager::get(); + QVariantMap deviceMap; + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + const auto inputName = sanatizeName(inputMapping.second); + qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() + << QString::number(input.getID(), 16) << ": " << inputName; + deviceMap.insert(inputName, input.getID()); + } + return deviceMap; + } + + NewControllerScriptingInterface::NewControllerScriptingInterface() { + auto userInputMapper = DependencyManager::get(); + auto devices = userInputMapper->getDevices(); + for (const auto& deviceMapping : devices) { + auto device = deviceMapping.second.get(); + auto deviceName = sanatizeName(device->getName()); + qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; + // Expose the IDs to JS + _hardware.insert(deviceName, createDeviceMap(device)); + + // Create the endpoints + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + // Ignore aliases + if (_endpoints.count(input)) { + continue; + } + _endpoints[input] = std::make_shared([=] { + auto deviceProxy = userInputMapper->getDeviceProxy(input); + if (!deviceProxy) { + return 0.0f; + } + return deviceProxy->getValue(input, 0); + }); + } + } + + qCDebug(controllers) << "Setting up standard controller abstraction"; + auto standardDevice = userInputMapper->getStandardDevice(); + // Expose the IDs to JS + _standard = createDeviceMap(standardDevice.get()); + // Create the endpoints + for (const auto& inputMapping : standardDevice->getAvailabeInputs()) { + const auto& standardInput = inputMapping.first; + // Ignore aliases + if (_endpoints.count(standardInput)) { + continue; + } + _endpoints[standardInput] = std::make_shared(standardInput); + } + + auto actionNames = userInputMapper->getActionNames(); + int actionNumber = 0; + qCDebug(controllers) << "Setting up standard actions"; + for (const auto& actionName : actionNames) { + UserInputMapper::Input actionInput(ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); + qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); + // Expose the IDs to JS + _actions.insert(sanatizeName(actionName), actionInput.getID()); + + // Create the endpoints + // FIXME action endpoints need to accumulate values, and have them cleared at each frame + _endpoints[actionInput] = std::make_shared(); + } + } + + QObject* NewControllerScriptingInterface::newMapping(const QString& mappingName) { + if (_mappingsByName.count(mappingName)) { + qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; + } + qDebug() << "Creating new Mapping " << mappingName; + Mapping::Pointer mapping = std::make_shared(); + _mappingsByName[mappingName] = mapping; + return new MappingBuilderProxy(*this, mapping); + } + + void NewControllerScriptingInterface::enableMapping(const QString& mappingName, bool enable) { + auto iterator = _mappingsByName.find(mappingName); + if (_mappingsByName.end() == iterator) { + qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; + return; + } + + auto mapping = iterator->second; + if (enable) { + _activeMappings.push_front(mapping); + } else { + auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping); + if (_activeMappings.end() == activeIterator) { + qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName; + return; + } + _activeMappings.erase(activeIterator); + } } float NewControllerScriptingInterface::getValue(const int& source) { - //UserInputMapper::Input input; input._id = source; - //auto userInputMapper = DependencyManager::get(); - //auto deviceProxy = userInputMapper->getDeviceProxy(input); - //return deviceProxy->getButton(input, 0) ? 1.0 : 0.0; + // return (sin(secTimestampNow()) + 1.0f) / 2.0f; + UserInputMapper::Input input(source); + auto iterator = _endpoints.find(input); + if (_endpoints.end() == iterator) { + return 0.0; + } - return (sin(secTimestampNow()) + 1.0f) / 2.0f; + const auto& endpoint = iterator->second; + return getValue(endpoint); + } + + float NewControllerScriptingInterface::getValue(const Endpoint::Pointer& endpoint) { + auto valuesIterator = _overrideValues.find(endpoint); + if (_overrideValues.end() != valuesIterator) { + return valuesIterator->second; + } + + return endpoint->value(); + } + + + void NewControllerScriptingInterface::update() { + static float last = secTimestampNow(); + float now = secTimestampNow(); + float delta = now - last; + last = now; + + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + inputPlugin->pluginUpdate(delta, false); + } + + auto userInputMapper = DependencyManager::get(); + userInputMapper->update(delta); + + _overrideValues.clear(); + EndpointSet readEndpoints; + EndpointSet writtenEndpoints; + // Now process the current values for each level of the stack + for (auto& mapping : _activeMappings) { + for (const auto& mappingEntry : mapping->_channelMappings) { + const auto& source = mappingEntry.first; + + // 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 + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // I press the button. The exception is if I'm wiring a control back to itself + // in order to adjust my interface, like inverting the Y axis on an analog stick + if (readEndpoints.count(source)) { + continue; + } + + // Apply the value to all the routes + const auto& routes = mappingEntry.second; + + for (const auto& route : routes) { + const auto& destination = route->_destination; + + if (writtenEndpoints.count(destination)) { + continue; + } + + // Standard controller destinations can only be can only be used once. + if (userInputMapper->getStandardDeviceID() == destination->getId().getDevice()) { + writtenEndpoints.insert(destination); + } + + // Only consume the input if the route isn't a loopback. + // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` + bool loopback = source == destination; + if (!loopback) { + readEndpoints.insert(source); + } + + // Fetch the value, may have been overriden by previous loopback routes + float value = getValue(source); + + // Apply each of the filters. + const auto& filters = route->_filters; + for (const auto& filter : route->_filters) { + value = filter->apply(value); + } + + if (loopback) { + _overrideValues[source] = value; + } else { + destination->apply(value, 0, source); + } + } + } + } + } + + + + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const QJSValue& endpoint) { + if (endpoint.isNumber()) { + return endpointFor(UserInputMapper::Input(endpoint.toInt())); + } + + if (endpoint.isCallable()) { + auto result = std::make_shared(endpoint); + return result; + } + + qWarning() << "Unsupported input type " << endpoint.toString(); + return Endpoint::Pointer(); + } + + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const QScriptValue& endpoint) { + if (endpoint.isNumber()) { + return endpointFor(UserInputMapper::Input(endpoint.toInt32())); + } + + qWarning() << "Unsupported input type " << endpoint.toString(); + return Endpoint::Pointer(); + } + + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { + auto iterator = _endpoints.find(inputId); + if (_endpoints.end() == iterator) { + qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); + return Endpoint::Pointer(); + } + return iterator->second; + } + + Endpoint::Pointer NewControllerScriptingInterface::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { + EndpointPair pair(first, second); + Endpoint::Pointer result; + auto iterator = _compositeEndpoints.find(pair); + if (_compositeEndpoints.end() == iterator) { + result = std::make_shared(first, second); + _compositeEndpoints[pair] = result; + } else { + result = iterator->second; + } + return result; } } // namespace controllers - - - -// class MappingsStack { -// std::list _stack; -// ValueMap _lastValues; -// -// void update() { -// EndpointList hardwareInputs = getHardwareEndpoints(); -// ValueMap currentValues; -// -// for (auto input : hardwareInputs) { -// currentValues[input] = input->value(); -// } -// -// // Now process the current values for each level of the stack -// for (auto& mapping : _stack) { -// update(mapping, currentValues); -// } -// -// _lastValues = currentValues; -// } -// -// void update(Mapping& mapping, ValueMap& values) { -// ValueMap updates; -// EndpointList consumedEndpoints; -// for (const auto& entry : values) { -// Endpoint* endpoint = entry.first; -// if (!mapping._channelMappings.count(endpoint)) { -// continue; -// } -// -// const Mapping::List& routes = mapping._channelMappings[endpoint]; -// consumedEndpoints.push_back(endpoint); -// for (const auto& route : routes) { -// float lastValue = 0; -// if (mapping._lastValues.count(endpoint)) { -// lastValue = mapping._lastValues[endpoint]; -// } -// float value = entry.second; -// for (const auto& filter : route._filters) { -// value = filter->apply(value, lastValue); -// } -// updates[route._destination] = value; -// } -// } -// -// // Update the last seen values -// mapping._lastValues = values; -// -// // Remove all the consumed inputs -// for (auto endpoint : consumedEndpoints) { -// values.erase(endpoint); -// } -// -// // Add all the updates (may restore some of the consumed data if a passthrough was created (i.e. source == dest) -// for (const auto& entry : updates) { -// values[entry.first] = entry.second; -// } -// } -// }; //var mapping = Controller.newMapping(); //mapping.map(hydra.LeftButton0, actions.ContextMenu); //mapping.map(hydra.LeftButton0).to(xbox.RT); @@ -134,5 +364,3 @@ namespace Controllers { // mappingSnap.from(hydra.LX).to(function(newValue, oldValue) { // timeSinceLastYaw += deltaTime - -#include "NewControllerScriptingInterface.moc" diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index c4c42a2245..6bf0bda40d 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -10,21 +10,78 @@ #ifndef hifi_Controllers_NewControllerScriptingInterface_h #define hifi_Controllers_NewControllerScriptingInterface_h +#include +#include +#include +#include + #include #include +#include +#include + +#include + #include "Mapping.h" class QScriptValue; -namespace Controllers { +namespace controller { class NewControllerScriptingInterface : public QObject { Q_OBJECT - + Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL) + Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL) + Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL) + public: - Q_INVOKABLE void update(); - Q_INVOKABLE QObject* newMapping(); + NewControllerScriptingInterface(); Q_INVOKABLE float getValue(const int& source); + + Q_INVOKABLE void update(); + Q_INVOKABLE QObject* newMapping(const QString& mappingName); + Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); + Q_INVOKABLE void disableMapping(const QString& mappingName) { + enableMapping(mappingName, false); + } + + + const QVariantMap& getHardware() { return _hardware; } + const QVariantMap& getActions() { return _actions; } + const QVariantMap& getStandard() { return _standard; } + + private: + + // FIXME move to unordered set / map + using MappingMap = std::map; + using MappingStack = std::list; + using InputToEndpointMap = std::map; + using EndpointSet = std::unordered_set; + using ValueMap = std::map; + using EndpointPair = std::pair; + using EndpointPairMap = std::map; + + void update(Mapping::Pointer& mapping, EndpointSet& consumed); + float getValue(const Endpoint::Pointer& endpoint); + Endpoint::Pointer endpointFor(const QJSValue& endpoint); + Endpoint::Pointer endpointFor(const QScriptValue& endpoint); + Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); + Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); + + friend class MappingBuilderProxy; + friend class RouteBuilderProxy; + private: + uint16_t _nextFunctionId; + InputToEndpointMap _endpoints; + EndpointPairMap _compositeEndpoints; + + ValueMap _overrideValues; + MappingMap _mappingsByName; + MappingStack _activeMappings; + + QVariantMap _hardware; + QVariantMap _actions; + QVariantMap _standard; }; } diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/Route.cpp index 29fb9b04eb..b9116d2813 100644 --- a/libraries/controllers/src/controllers/Route.cpp +++ b/libraries/controllers/src/controllers/Route.cpp @@ -12,10 +12,11 @@ #include "Endpoint.h" #include "Filter.h" +#include "Logging.h" class QJsonObject; -namespace Controllers { +namespace controller { /* * encapsulates a source, destination and filters to apply diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h index 9459369d18..01770a87d1 100644 --- a/libraries/controllers/src/controllers/Route.h +++ b/libraries/controllers/src/controllers/Route.h @@ -15,7 +15,7 @@ class QJsonObject; -namespace Controllers { +namespace controller { /* * encapsulates a source, destination and filters to apply diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index e16fa511db..4e2c6a4d8c 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -12,22 +12,33 @@ #include #include "RouteBuilderProxy.h" +#include "../NewControllerScriptingInterface.h" +#include "../Logging.h" -namespace Controllers { +namespace controller { -QObject* MappingBuilderProxy::from(const QString& source) { - qDebug() << "Creating new Route builder proxy from " << source; +QObject* MappingBuilderProxy::from(const QJSValue& source) { + qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); + auto sourceEndpoint = _parent.endpointFor(source); + return from(sourceEndpoint); +} + +QObject* MappingBuilderProxy::from(const QScriptValue& source) { + qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); + auto sourceEndpoint = _parent.endpointFor(source); + return from(sourceEndpoint); +} + +QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { auto route = Route::Pointer(new Route()); - route->_source = endpointFor(source); - return new RouteBuilderProxy(this, route); + route->_source = source; + return new RouteBuilderProxy(_parent, _mapping, route); } -Endpoint::Pointer MappingBuilderProxy::endpointFor(const QString& endpoint) { - static QHash ENDPOINTS; - if (!ENDPOINTS.contains(endpoint)) { - ENDPOINTS[endpoint] = std::make_shared(); - } - return ENDPOINTS[endpoint]; +QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& source2) { + auto source1Endpoint = _parent.endpointFor(source1); + auto source2Endpoint = _parent.endpointFor(source2); + return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 6dac38b21e..b5e02bbfdf 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -13,20 +13,30 @@ #include #include "../Mapping.h" +#include "../Endpoint.h" -namespace Controllers { +class QJSValue; +class QScriptValue; + +namespace controller { + +class NewControllerScriptingInterface; class MappingBuilderProxy : public QObject { Q_OBJECT public: - MappingBuilderProxy(Mapping::Pointer mapping) - : _mapping(mapping) { } + MappingBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping) + : _parent(parent), _mapping(mapping) { } - Q_INVOKABLE QObject* from(const QString& fromEndpoint); + Q_INVOKABLE QObject* from(const QJSValue& source); + Q_INVOKABLE QObject* from(const QScriptValue& source); + Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); protected: + QObject* from(const Endpoint::Pointer& source); + friend class RouteBuilderProxy; - Endpoint::Pointer endpointFor(const QString& endpoint); + NewControllerScriptingInterface& _parent; Mapping::Pointer _mapping; }; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index d1659573e4..e6b67e9ca6 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -12,17 +12,47 @@ #include #include "MappingBuilderProxy.h" +#include "../NewControllerScriptingInterface.h" +#include "../Logging.h" -namespace Controllers { +namespace controller { -void RouteBuilderProxy::to(const QString& destination) { - qDebug() << "Completed route: " << destination; +void RouteBuilderProxy::to(const QJSValue& destination) { + qCDebug(controllers) << "Completing route " << destination.toString(); + auto destinationEndpoint = _parent.endpointFor(destination); + return to(destinationEndpoint); +} + +void RouteBuilderProxy::to(const QScriptValue& destination) { + qCDebug(controllers) << "Completing route " << destination.toString(); + auto destinationEndpoint = _parent.endpointFor(destination); + return to(destinationEndpoint); +} + +void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { auto sourceEndpoint = _route->_source; - auto& mapping = _parent->_mapping; - mapping->_channelMappings[sourceEndpoint].push_back(_route); + _route->_destination = destination; + _mapping->_channelMappings[sourceEndpoint].push_back(_route); deleteLater(); } +QObject* RouteBuilderProxy::filter(const QJSValue& expression) { + if (expression.isCallable()) { + addFilter([=](float value) { + QJSValue originalExpression = expression; + QJSValueList params({ QJSValue(value) }); + auto result = originalExpression.call(params); + return (float)(result.toNumber()); + }); + } + return this; +} + +QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { + return this; +} + + QObject* RouteBuilderProxy::clamp(float min, float max) { addFilter([=](float value) { return glm::clamp(value, min, max); @@ -45,7 +75,7 @@ QObject* RouteBuilderProxy::deadZone(float min) { assert(min < 1.0f); float scale = 1.0f / (1.0f - min); addFilter([=](float value) { - if (value < min) { + if (abs(value) < min) { return 0.0f; } return (value - min) * scale; @@ -68,6 +98,40 @@ QObject* RouteBuilderProxy::constrainToPositiveInteger() { return this; } + +class PulseFilter : public Filter { +public: + PulseFilter(float interval) : _interval(interval) {} + + virtual float apply(float value) const override { + float result = 0.0; + + if (0.0 != value) { + float now = secTimestampNow(); + float delta = now - _lastEmitTime; + if (delta >= _interval) { + _lastEmitTime = now; + result = value; + } + } + + return result; + } + +private: + mutable float _lastEmitTime{ -std::numeric_limits::max() }; + const float _interval; +}; + + +QObject* RouteBuilderProxy::pulse(float interval) { + Filter::Pointer filter = std::make_shared(interval); + addFilter(filter); + return this; +} + + + void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda); addFilter(filterPointer); diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 516712b969..63cd106edb 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -12,19 +12,28 @@ #include #include "../Filter.h" #include "../Route.h" +#include "../Mapping.h" -namespace Controllers { +class QJSValue; +class QScriptValue; -class MappingBuilderProxy; +namespace controller { + +class NewControllerScriptingInterface; class RouteBuilderProxy : public QObject { Q_OBJECT public: - RouteBuilderProxy(MappingBuilderProxy* parent, Route::Pointer route) - : _parent(parent), _route(route) { } + RouteBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route) + : _parent(parent), _mapping(mapping), _route(route) { } - Q_INVOKABLE void to(const QString& destination); + Q_INVOKABLE void to(const QJSValue& destination); + Q_INVOKABLE void to(const QScriptValue& destination); + + Q_INVOKABLE QObject* filter(const QJSValue& expression); + Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); + Q_INVOKABLE QObject* pulse(float interval); Q_INVOKABLE QObject* scale(float multiplier); Q_INVOKABLE QObject* invert(); Q_INVOKABLE QObject* deadZone(float min); @@ -32,10 +41,13 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToPositiveInteger(); private: + void to(const Endpoint::Pointer& destination); void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); + Mapping::Pointer _mapping; Route::Pointer _route; - MappingBuilderProxy* _parent; + + NewControllerScriptingInterface& _parent; }; } diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 227bd12e1b..b52dd3f658 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -22,8 +22,8 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), new SDL2Manager(), - new SixenseManager(), - new ViveControllerManager(), + //new SixenseManager(), + //new ViveControllerManager(), nullptr }; diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index d0e2705e98..5c6f43c604 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -15,6 +15,8 @@ #include "Joystick.h" +#include "StandardControls.h" + const float CONTROLLER_THRESHOLD = 0.3f; #ifdef HAVE_SDL2 @@ -55,39 +57,14 @@ void Joystick::focusOutEvent() { }; #ifdef HAVE_SDL2 + void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; - - switch (axis) { - case SDL_CONTROLLER_AXIS_LEFTX: - _axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_LEFTY: - _axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_RIGHTX: - _axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_RIGHTY: - _axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; - break; - case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - _axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS; - break; - case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS; - break; - default: - break; - } + _axisStateMap[makeInput((Controllers::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; } void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { - auto input = makeInput((SDL_GameControllerButton) event.button); + auto input = makeInput((Controllers::StandardButtonChannel)event.button); bool newValue = event.state == SDL_PRESSED; if (newValue) { _buttonPressedMap.insert(input.getChannel()); @@ -95,6 +72,7 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { _buttonPressedMap.erase(input.getChannel()); } } + #endif @@ -107,32 +85,57 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; -#ifdef HAVE_SDL2 - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right")); -#endif return availableInputs; }; proxy->resetDeviceBindings = [this, &mapper] () -> bool { @@ -150,76 +153,15 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { const float JOYSTICK_YAW_SPEED = 0.5f; const float JOYSTICK_PITCH_SPEED = 0.25f; const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); - - - // Hold front right shoulder button for precision controls - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A)); + #endif } -#ifdef HAVE_SDL2 -UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) { +UserInputMapper::Input Joystick::makeInput(Controllers::StandardButtonChannel button) { return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); } -#endif -UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) { +UserInputMapper::Input Joystick::makeInput(Controllers::StandardAxisChannel axis) { return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); } diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index 2ba89da052..70949a8b83 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -21,6 +21,7 @@ #endif #include "InputDevice.h" +#include "StandardControls.h" class Joystick : public QObject, public InputDevice { Q_OBJECT @@ -31,18 +32,6 @@ class Joystick : public QObject, public InputDevice { #endif public: - enum JoystickAxisChannel { - LEFT_AXIS_X_POS = 0, - LEFT_AXIS_X_NEG, - LEFT_AXIS_Y_POS, - LEFT_AXIS_Y_NEG, - RIGHT_AXIS_X_POS, - RIGHT_AXIS_X_NEG, - RIGHT_AXIS_Y_POS, - RIGHT_AXIS_Y_NEG, - RIGHT_SHOULDER, - LEFT_SHOULDER, - }; const QString& getName() const { return _name; } @@ -55,10 +44,8 @@ public: Joystick() : InputDevice("Joystick") {} ~Joystick(); -#ifdef HAVE_SDL2 - UserInputMapper::Input makeInput(SDL_GameControllerButton button); -#endif - UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis); + UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button); + UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis); #ifdef HAVE_SDL2 Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 36ae643a8e..202a767244 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -10,6 +10,11 @@ // #include "KeyboardMouseDevice.h" +#include +#include +#include + + const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { @@ -81,7 +86,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); } -glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList& points) const { +glm::vec2 evalAverageTouchPoints(const QList& points) { glm::vec2 averagePoint(0.0f); if (points.count() > 0) { for (auto& point : points) { diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 6f703bc6f9..d96566e9d1 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -12,11 +12,15 @@ #ifndef hifi_KeyboardMouseDevice_h #define hifi_KeyboardMouseDevice_h -#include #include #include "InputDevice.h" #include "InputPlugin.h" +class QTouchEvent; +class QKeyEvent; +class QMouseEvent; +class QWheelEvent; + class KeyboardMouseDevice : public InputPlugin, public InputDevice { Q_OBJECT public: @@ -100,7 +104,6 @@ protected: glm::vec2 _lastTouch; bool _isTouching = false; - glm::vec2 evalAverageTouchPoints(const QList& points) const; std::chrono::high_resolution_clock _clock; std::chrono::high_resolution_clock::time_point _lastTouchTime; }; diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/input-plugins/src/input-plugins/StandardController.cpp index 4fb4a23654..040efca794 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/input-plugins/src/input-plugins/StandardController.cpp @@ -23,11 +23,6 @@ StandardController::~StandardController() { } void StandardController::update(float deltaTime, bool jointsCaptured) { - for (auto axisState : _axisStateMap) { - if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { - _axisStateMap[axisState.first] = 0.0f; - } - } } void StandardController::focusOutEvent() { @@ -44,119 +39,85 @@ void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_A), "Bottom Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_B), "Right Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_X), "Left Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_Y), "Top Button")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT")); + + // Poses + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LeftPose), "LeftPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RightPose), "RightPose")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand")); return availableInputs; }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { mapper.removeAllInputChannelsForDevice(_deviceID); this->assignDefaultInputMapping(mapper); return true; }; + mapper.registerStandardDevice(proxy); } void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // Left StandardController: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); - - // Right StandardController: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); - - - // Hold front right shoulder button for precision controls - // Left StandardController: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); - - // Right StandardController: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(STANDARD_CONTROLLER_BUTTON_B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(STANDARD_CONTROLLER_BUTTON_A)); } -UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerButtonChannel button) { +UserInputMapper::Input StandardController::makeInput(Controllers::StandardButtonChannel button) { return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); } -UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerAxisChannel axis) { +UserInputMapper::Input StandardController::makeInput(Controllers::StandardAxisChannel axis) { return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); } -UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerPoseChannel pose) { +UserInputMapper::Input StandardController::makeInput(Controllers::StandardPoseChannel pose) { return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); } diff --git a/libraries/input-plugins/src/input-plugins/StandardController.h b/libraries/input-plugins/src/input-plugins/StandardController.h index f7a4215242..fa660e15b8 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.h +++ b/libraries/input-plugins/src/input-plugins/StandardController.h @@ -17,6 +17,8 @@ #include "InputDevice.h" +#include "StandardControls.h" + typedef std::shared_ptr StandardControllerPointer; class StandardController : public QObject, public InputDevice { @@ -24,37 +26,6 @@ class StandardController : public QObject, public InputDevice { Q_PROPERTY(QString name READ getName) public: - enum StandardControllerAxisChannel { - LEFT_AXIS_X_POS = 0, - LEFT_AXIS_X_NEG, - LEFT_AXIS_Y_POS, - LEFT_AXIS_Y_NEG, - RIGHT_AXIS_X_POS, - RIGHT_AXIS_X_NEG, - RIGHT_AXIS_Y_POS, - RIGHT_AXIS_Y_NEG, - RIGHT_SHOULDER, - LEFT_SHOULDER, - }; - enum StandardControllerButtonChannel { - STANDARD_CONTROLLER_BUTTON_A = 0, - STANDARD_CONTROLLER_BUTTON_B, - STANDARD_CONTROLLER_BUTTON_X, - STANDARD_CONTROLLER_BUTTON_Y, - - STANDARD_CONTROLLER_BUTTON_DPAD_UP, - STANDARD_CONTROLLER_BUTTON_DPAD_DOWN, - STANDARD_CONTROLLER_BUTTON_DPAD_LEFT, - STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT, - - STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER, - STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER, - }; - - enum StandardControllerPoseChannel { - LEFT_HAND = 0, - RIGHT_HAND, - }; const QString& getName() const { return _name; } @@ -67,9 +38,9 @@ public: StandardController() : InputDevice("Standard") {} ~StandardController(); - UserInputMapper::Input makeInput(StandardController::StandardControllerButtonChannel button); - UserInputMapper::Input makeInput(StandardController::StandardControllerAxisChannel axis); - UserInputMapper::Input makeInput(StandardController::StandardControllerPoseChannel pose); + UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button); + UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis); + UserInputMapper::Input makeInput(Controllers::StandardPoseChannel pose); private: }; diff --git a/libraries/input-plugins/src/input-plugins/StandardControls.h b/libraries/input-plugins/src/input-plugins/StandardControls.h new file mode 100644 index 0000000000..9b79bbdae1 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/StandardControls.h @@ -0,0 +1,55 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 + +namespace Controllers { + + // Needs to match order and values of SDL_GameControllerButton + enum StandardButtonChannel { + // Button quad + A = 0, + B, + X, + Y, + // Center buttons + BACK, + GUIDE, + START, + // Stick press + LS, + RS, + // Bumper press + LB, + RB, + // DPad + DU, + DD, + DL, + DR + }; + + // Needs to match order and values of SDL_GameControllerAxis + enum StandardAxisChannel { + // Left Analog stick + LX = 0, + LY, + // Right Analog stick + RX, + RY, + // Triggers + LT, + RT + }; + + // No correlation to SDL + enum StandardPoseChannel { + LeftPose = 0, + RightPose + }; + +} diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index fad962345c..c29acc09af 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -1,335 +1,368 @@ -// -// UserInputMapper.cpp -// input-plugins/src/input-plugins -// -// Created by Sam Gateau on 4/27/15. -// 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 -// - -#include "UserInputMapper.h" -#include "StandardController.h" - -// Default contruct allocate the poutput size with the current hardcoded action channels -UserInputMapper::UserInputMapper() { - registerStandardDevice(); - assignDefaulActionScales(); - createActionNames(); -} - -UserInputMapper::~UserInputMapper() { -} - - -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ - proxy->_name += " (" + QString::number(deviceID) + ")"; - _registeredDevices[deviceID] = proxy; - return true; -} - -UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { - auto device = _registeredDevices.find(input.getDevice()); - if (device != _registeredDevices.end()) { - return (device->second); - } else { - return DeviceProxy::Pointer(); - } -} - -QString UserInputMapper::getDeviceName(uint16 deviceID) { - if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->_name; - } - return QString("unknown"); -} - - -void UserInputMapper::resetAllDeviceBindings() { - for (auto device : _registeredDevices) { - device.second->resetDeviceBindings(); - } -} - -void UserInputMapper::resetDevice(uint16 deviceID) { - auto device = _registeredDevices.find(deviceID); - if (device != _registeredDevices.end()) { - device->second->resetDeviceBindings(); - } -} - -int UserInputMapper::findDevice(QString name) { - for (auto device : _registeredDevices) { - if (device.second->_name.split(" (")[0] == name) { - return device.first; - } - } - return 0; -} - -QVector UserInputMapper::getDeviceNames() { - QVector result; - for (auto device : _registeredDevices) { - QString deviceName = device.second->_name.split(" (")[0]; - result << deviceName; - } - return result; -} - - -bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { - return addInputChannel(action, input, Input(), scale); -} - -bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { - // Check that the device is registered - if (!getDeviceProxy(input)) { - qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; - return false; - } - - auto inputChannel = InputChannel(input, modifier, action, scale); - - // Insert or replace the input to modifiers - if (inputChannel.hasModifier()) { - auto& modifiers = _inputToModifiersMap[input.getID()]; - modifiers.push_back(inputChannel._modifier); - std::sort(modifiers.begin(), modifiers.end()); - } - - // Now update the action To Inputs side of things - _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); - - return true; -} - -int UserInputMapper::addInputChannels(const InputChannels& channels) { - int nbAdded = 0; - for (auto& channel : channels) { - nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); - } - return nbAdded; -} - -bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { - // Remove from Input to Modifiers map - if (inputChannel.hasModifier()) { - _inputToModifiersMap.erase(inputChannel._input.getID()); - } - - // Remove from Action to Inputs map - std::pair ret; - ret = _actionToInputsMap.equal_range(inputChannel._action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - if (it->second == inputChannel) { - _actionToInputsMap.erase(it); - return true; - } - } - - return false; -} - -void UserInputMapper::removeAllInputChannels() { - _inputToModifiersMap.clear(); - _actionToInputsMap.clear(); -} - -void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { - QVector channels = getAllInputsForDevice(device); - for (auto& channel : channels) { - removeInputChannel(channel); - } -} - -void UserInputMapper::removeDevice(int device) { - removeAllInputChannelsForDevice((uint16) device); - _registeredDevices.erase(device); -} - -int UserInputMapper::getInputChannels(InputChannels& channels) const { - for (auto& channel : _actionToInputsMap) { - channels.push_back(channel.second); - } - - return _actionToInputsMap.size(); -} - -QVector UserInputMapper::getAllInputsForDevice(uint16 device) { - InputChannels allChannels; - getInputChannels(allChannels); - - QVector channels; - for (InputChannel inputChannel : allChannels) { - if (inputChannel._input._device == device) { - channels.push_back(inputChannel); - } - } - - return channels; -} - -void UserInputMapper::update(float deltaTime) { - - // Reset the axis state for next loop - for (auto& channel : _actionStates) { - channel = 0.0f; - } - - for (auto& channel : _poseStates) { - channel = PoseValue(); - } - - int currentTimestamp = 0; - - for (auto& channelInput : _actionToInputsMap) { - auto& inputMapping = channelInput.second; - auto& inputID = inputMapping._input; - bool enabled = true; - - // Check if this input channel has modifiers and collect the possibilities - auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); - if (modifiersIt != _inputToModifiersMap.end()) { - Modifiers validModifiers; - bool isActiveModifier = false; - for (auto& modifier : modifiersIt->second) { - auto deviceProxy = getDeviceProxy(modifier); - if (deviceProxy->getButton(modifier, currentTimestamp)) { - validModifiers.push_back(modifier); - isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); - } - } - enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; - } - - // if enabled: default input or all modifiers on - if (enabled) { - auto deviceProxy = getDeviceProxy(inputID); - switch (inputMapping._input.getType()) { - case ChannelType::BUTTON: { - _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime - break; - } - case ChannelType::AXIS: { - _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); - break; - } - case ChannelType::POSE: { - if (!_poseStates[channelInput.first].isValid()) { - _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); - } - break; - } - default: { - break; //silence please - } - } - } else{ - // Channel input not enabled - enabled = false; - } - } - - // Scale all the channel step with the scale - static const float EPSILON = 0.01f; - for (auto i = 0; i < NUM_ACTIONS; i++) { - _actionStates[i] *= _actionScales[i]; - // Emit only on change, and emit when moving back to 0 - if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { - _lastActionStates[i] = _actionStates[i]; - emit actionEvent(i, _actionStates[i]); - } - // TODO: emit signal for pose changes - } -} - -QVector UserInputMapper::getAllActions() const { - QVector actions; - for (auto i = 0; i < NUM_ACTIONS; i++) { - actions.append(Action(i)); - } - return actions; -} - -QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { - QVector inputChannels; - std::pair ret; - ret = _actionToInputsMap.equal_range(action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - inputChannels.append(it->second); - } - return inputChannels; -} - -int UserInputMapper::findAction(const QString& actionName) const { - auto actions = getAllActions(); - for (auto action : actions) { - if (getActionName(action) == actionName) { - return action; - } - } - // If the action isn't found, return -1 - return -1; -} - -QVector UserInputMapper::getActionNames() const { - QVector result; - for (auto i = 0; i < NUM_ACTIONS; i++) { - result << _actionNames[i]; - } - return result; -} - -void UserInputMapper::assignDefaulActionScales() { - _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit - _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit - _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit - _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit - _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit - _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit - _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit - _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit - _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit - _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit - _actionScales[BOOM_IN] = 0.5f; // .5m per unit - _actionScales[BOOM_OUT] = 0.5f; // .5m per unit - _actionScales[LEFT_HAND] = 1.0f; // default - _actionScales[RIGHT_HAND] = 1.0f; // default - _actionScales[LEFT_HAND_CLICK] = 1.0f; // on - _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on - _actionStates[SHIFT] = 1.0f; // on - _actionStates[ACTION1] = 1.0f; // default - _actionStates[ACTION2] = 1.0f; // default -} - -// This is only necessary as long as the actions are hardcoded -// Eventually you can just add the string when you add the action -void UserInputMapper::createActionNames() { - _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; - _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; - _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; - _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; - _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; - _actionNames[VERTICAL_UP] = "VERTICAL_UP"; - _actionNames[YAW_LEFT] = "YAW_LEFT"; - _actionNames[YAW_RIGHT] = "YAW_RIGHT"; - _actionNames[PITCH_DOWN] = "PITCH_DOWN"; - _actionNames[PITCH_UP] = "PITCH_UP"; - _actionNames[BOOM_IN] = "BOOM_IN"; - _actionNames[BOOM_OUT] = "BOOM_OUT"; - _actionNames[LEFT_HAND] = "LEFT_HAND"; - _actionNames[RIGHT_HAND] = "RIGHT_HAND"; - _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; - _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; - _actionNames[SHIFT] = "SHIFT"; - _actionNames[ACTION1] = "ACTION1"; - _actionNames[ACTION2] = "ACTION2"; - _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; - _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; -} - -void UserInputMapper::registerStandardDevice() { - _standardController = std::make_shared(); - _standardController->registerToUserInputMapper(*this); -} \ No newline at end of file +// +// UserInputMapper.cpp +// input-plugins/src/input-plugins +// +// Created by Sam Gateau on 4/27/15. +// 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 +// + +#include "UserInputMapper.h" +#include "StandardController.h" + +const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX); +const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); +const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); +const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); + +// Default contruct allocate the poutput size with the current hardcoded action channels +UserInputMapper::UserInputMapper() { + registerStandardDevice(); + assignDefaulActionScales(); + createActionNames(); +} + +UserInputMapper::~UserInputMapper() { +} + + +bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ + proxy->_name += " (" + QString::number(deviceID) + ")"; + _registeredDevices[deviceID] = proxy; + return true; +} + +UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { + auto device = _registeredDevices.find(input.getDevice()); + if (device != _registeredDevices.end()) { + return (device->second); + } else { + return DeviceProxy::Pointer(); + } +} + +QString UserInputMapper::getDeviceName(uint16 deviceID) { + if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { + return _registeredDevices[deviceID]->_name; + } + return QString("unknown"); +} + + +void UserInputMapper::resetAllDeviceBindings() { + for (auto device : _registeredDevices) { + device.second->resetDeviceBindings(); + } +} + +void UserInputMapper::resetDevice(uint16 deviceID) { + auto device = _registeredDevices.find(deviceID); + if (device != _registeredDevices.end()) { + device->second->resetDeviceBindings(); + } +} + +int UserInputMapper::findDevice(QString name) { + for (auto device : _registeredDevices) { + if (device.second->_name.split(" (")[0] == name) { + return device.first; + } + } + return 0; +} + +QVector UserInputMapper::getDeviceNames() { + QVector result; + for (auto device : _registeredDevices) { + QString deviceName = device.second->_name.split(" (")[0]; + result << deviceName; + } + return result; +} + + +bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { + return addInputChannel(action, input, Input(), scale); +} + +bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { + // Check that the device is registered + if (!getDeviceProxy(input)) { + qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; + return false; + } + + auto inputChannel = InputChannel(input, modifier, action, scale); + + // Insert or replace the input to modifiers + if (inputChannel.hasModifier()) { + auto& modifiers = _inputToModifiersMap[input.getID()]; + modifiers.push_back(inputChannel._modifier); + std::sort(modifiers.begin(), modifiers.end()); + } + + // Now update the action To Inputs side of things + _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); + + return true; +} + +int UserInputMapper::addInputChannels(const InputChannels& channels) { + int nbAdded = 0; + for (auto& channel : channels) { + nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); + } + return nbAdded; +} + +bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { + // Remove from Input to Modifiers map + if (inputChannel.hasModifier()) { + _inputToModifiersMap.erase(inputChannel._input.getID()); + } + + // Remove from Action to Inputs map + std::pair ret; + ret = _actionToInputsMap.equal_range(inputChannel._action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + if (it->second == inputChannel) { + _actionToInputsMap.erase(it); + return true; + } + } + + return false; +} + +void UserInputMapper::removeAllInputChannels() { + _inputToModifiersMap.clear(); + _actionToInputsMap.clear(); +} + +void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { + QVector channels = getAllInputsForDevice(device); + for (auto& channel : channels) { + removeInputChannel(channel); + } +} + +void UserInputMapper::removeDevice(int device) { + removeAllInputChannelsForDevice((uint16) device); + _registeredDevices.erase(device); +} + +int UserInputMapper::getInputChannels(InputChannels& channels) const { + for (auto& channel : _actionToInputsMap) { + channels.push_back(channel.second); + } + + return _actionToInputsMap.size(); +} + +QVector UserInputMapper::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector channels; + for (InputChannel inputChannel : allChannels) { + if (inputChannel._input._device == device) { + channels.push_back(inputChannel); + } + } + + return channels; +} + +void UserInputMapper::update(float deltaTime) { + + // Reset the axis state for next loop + for (auto& channel : _actionStates) { + channel = 0.0f; + } + + for (auto& channel : _poseStates) { + channel = PoseValue(); + } + + int currentTimestamp = 0; + + for (auto& channelInput : _actionToInputsMap) { + auto& inputMapping = channelInput.second; + auto& inputID = inputMapping._input; + bool enabled = true; + + // Check if this input channel has modifiers and collect the possibilities + auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); + if (modifiersIt != _inputToModifiersMap.end()) { + Modifiers validModifiers; + bool isActiveModifier = false; + for (auto& modifier : modifiersIt->second) { + auto deviceProxy = getDeviceProxy(modifier); + if (deviceProxy->getButton(modifier, currentTimestamp)) { + validModifiers.push_back(modifier); + isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); + } + } + enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; + } + + // if enabled: default input or all modifiers on + if (enabled) { + auto deviceProxy = getDeviceProxy(inputID); + switch (inputMapping._input.getType()) { + case ChannelType::BUTTON: { + _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime + break; + } + case ChannelType::AXIS: { + _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); + break; + } + case ChannelType::POSE: { + if (!_poseStates[channelInput.first].isValid()) { + _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); + } + break; + } + default: { + break; //silence please + } + } + } else{ + // Channel input not enabled + enabled = false; + } + } + + // Scale all the channel step with the scale + static const float EPSILON = 0.01f; + for (auto i = 0; i < NUM_ACTIONS; i++) { + _actionStates[i] *= _actionScales[i]; + // Emit only on change, and emit when moving back to 0 + if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { + _lastActionStates[i] = _actionStates[i]; + emit actionEvent(i, _actionStates[i]); + } + // TODO: emit signal for pose changes + } +} + +QVector UserInputMapper::getAllActions() const { + QVector actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector inputChannels; + std::pair ret; + ret = _actionToInputsMap.equal_range(action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + inputChannels.append(it->second); + } + return inputChannels; +} + +int UserInputMapper::findAction(const QString& actionName) const { + auto actions = getAllActions(); + for (auto action : actions) { + if (getActionName(action) == actionName) { + return action; + } + } + // If the action isn't found, return -1 + return -1; +} + +QVector UserInputMapper::getActionNames() const { + QVector result; + for (auto i = 0; i < NUM_ACTIONS; i++) { + result << _actionNames[i]; + } + return result; +} + +void UserInputMapper::assignDefaulActionScales() { + _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit + _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit + _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit + _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit + _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit + _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit + _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit + _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit + _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit + _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit + _actionScales[BOOM_IN] = 0.5f; // .5m per unit + _actionScales[BOOM_OUT] = 0.5f; // .5m per unit + _actionScales[LEFT_HAND] = 1.0f; // default + _actionScales[RIGHT_HAND] = 1.0f; // default + _actionScales[LEFT_HAND_CLICK] = 1.0f; // on + _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on + _actionStates[SHIFT] = 1.0f; // on + _actionStates[ACTION1] = 1.0f; // default + _actionStates[ACTION2] = 1.0f; // default + _actionStates[TranslateX] = 1.0f; // default + _actionStates[TranslateY] = 1.0f; // default + _actionStates[TranslateZ] = 1.0f; // default + _actionStates[Roll] = 1.0f; // default + _actionStates[Pitch] = 1.0f; // default + _actionStates[Yaw] = 1.0f; // default +} + +// This is only necessary as long as the actions are hardcoded +// Eventually you can just add the string when you add the action +void UserInputMapper::createActionNames() { + _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; + _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; + _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; + _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; + _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; + _actionNames[VERTICAL_UP] = "VERTICAL_UP"; + _actionNames[YAW_LEFT] = "YAW_LEFT"; + _actionNames[YAW_RIGHT] = "YAW_RIGHT"; + _actionNames[PITCH_DOWN] = "PITCH_DOWN"; + _actionNames[PITCH_UP] = "PITCH_UP"; + _actionNames[BOOM_IN] = "BOOM_IN"; + _actionNames[BOOM_OUT] = "BOOM_OUT"; + _actionNames[LEFT_HAND] = "LEFT_HAND"; + _actionNames[RIGHT_HAND] = "RIGHT_HAND"; + _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; + _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; + _actionNames[SHIFT] = "SHIFT"; + _actionNames[ACTION1] = "ACTION1"; + _actionNames[ACTION2] = "ACTION2"; + _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; + _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; + _actionNames[TranslateX] = "TranslateX"; + _actionNames[TranslateY] = "TranslateY"; + _actionNames[TranslateZ] = "TranslateZ"; + _actionNames[Roll] = "Roll"; + _actionNames[Pitch] = "Pitch"; + _actionNames[Yaw] = "Yaw"; +} + +void UserInputMapper::registerStandardDevice() { + _standardController = std::make_shared(); + _standardController->registerToUserInputMapper(*this); +} + +float UserInputMapper::DeviceProxy::getValue(const Input& input, int timestamp) const { + switch (input.getType()) { + case UserInputMapper::ChannelType::BUTTON: + return getButton(input, timestamp) ? 1.0f : 0.0f; + + case UserInputMapper::ChannelType::AXIS: + return getAxis(input, timestamp); + + case UserInputMapper::ChannelType::POSE: + return getPose(input, timestamp)._valid ? 1.0f : 0.0f; + + default: + return 0.0f; + } +} diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index 1d64638ee1..304e74e8cc 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -48,8 +48,9 @@ public: union { struct { uint16 _device; // Up to 64K possible devices - uint16 _channel : 14; // 2^14 possible channel per Device + uint16 _channel : 13; // 2^13 possible channel per Device uint16 _type : 2; // 2 bits to store the Type directly in the ID + uint16 _padding : 1; // 2 bits to store the Type directly in the ID }; uint32 _id = 0; // by default Input is 0 meaning invalid }; @@ -74,13 +75,19 @@ public: // where the default initializer (a C++-11ism) for the union data above is not applied. explicit Input() : _id(0) {} explicit Input(uint32 id) : _id(id) {} - explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {} + explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)), _padding(0) {} Input(const Input& src) : _id(src._id) {} Input& operator = (const Input& src) { _id = src._id; return (*this); } bool operator ==(const Input& right) const { return _id == right._id; } bool operator < (const Input& src) const { return _id < src._id; } + + static const Input INVALID_INPUT; + static const uint16 INVALID_DEVICE; + static const uint16 INVALID_CHANNEL; + static const uint16 INVALID_TYPE; }; + // Modifiers are just button inputID typedef std::vector< Input > Modifiers; @@ -121,7 +128,7 @@ public: PoseGetter getPose = [] (const Input& input, int timestamp) -> PoseValue { return PoseValue(); }; AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector(); }; ResetBindings resetDeviceBindings = [] () -> bool { return true; }; - + float getValue(const Input& input, int timestamp = 0) const; typedef std::shared_ptr Pointer; }; // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. @@ -171,6 +178,13 @@ public: CONTEXT_MENU, TOGGLE_MUTE, + TranslateX, + TranslateY, + TranslateZ, + Roll, + Pitch, + Yaw, + NUM_ACTIONS, }; diff --git a/tests/controllers/qml/Xbox.qml b/tests/controllers/qml/Xbox.qml new file mode 100644 index 0000000000..ae66081162 --- /dev/null +++ b/tests/controllers/qml/Xbox.qml @@ -0,0 +1,99 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./xbox" +import "./controls" + +Item { + id: root + + property var device + + property real scale: 1.0 + width: 300 * scale + height: 215 * scale + + Image { + anchors.fill: parent + source: "xbox/xbox360-controller-md.png" + + LeftAnalogStick { + device: root.device + x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 + } + + // Left stick press + ToggleButton { + controlId: root.device.LS + width: 16 * root.scale; height: 16 * root.scale + x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 + } + + + RightAnalogStick { + device: root.device + x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 + } + + // Right stick press + ToggleButton { + controlId: root.device.RS + width: 16 * root.scale; height: 16 * root.scale + x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 + } + + // Left trigger + AnalogButton { + controlId: root.device.LT + width: 8; height: 64 + x: (20 * root.scale); y: (7 * root.scale) + } + + // Right trigger + AnalogButton { + controlId: root.device.RT + width: 8; height: 64 + x: (272 * root.scale); y: (7 * root.scale) + } + + // Left bumper + ToggleButton { + controlId: root.device.LB + width: 32 * root.scale; height: 16 * root.scale + x: (40 * root.scale); y: (7 * root.scale) + } + + // Right bumper + ToggleButton { + controlId: root.device.RB + width: 32 * root.scale; height: 16 * root.scale + x: (root.width - width) - (40 * root.scale); y: (7 * root.scale) + } + + DPad { + device: root.device + size: 48 * root.scale + x: (80 * root.scale); y: (71 * root.scale) + } + + XboxButtons { + device: root.device + size: 65 * root.scale + x: (206 * root.scale); y: (19 * root.scale) + } + + ToggleButton { + controlId: root.device.Back + width: 16 * root.scale; height: 12 * root.scale + x: (112 * root.scale); y: (45 * root.scale) + } + + ToggleButton { + controlId: root.device.Start + width: 16 * root.scale; height: 12 * root.scale + x: (177 * root.scale); y: (45 * root.scale) + } + } +} diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index ac171ac42c..ce8a491419 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -1,137 +1,97 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -Rectangle { - id: root - implicitHeight: column1.height + 24 - implicitWidth: column1.width + 24 - color: "lightgray" - - property real itemSize: 128 - - Component { - id: graphTemplate - Item { - implicitHeight: canvas.height + 2 + text.height - implicitWidth: canvas.width - property string text: loadText - - Canvas { - id: canvas - width: root.itemSize; height: root.itemSize; - antialiasing: false - property int controlId: control - property real value: 0.0 - property int drawWidth: 1 - - Timer { - interval: 50; running: true; repeat: true - onTriggered: { - parent.value = NewControllers.getValue(canvas.controlId) - parent.requestPaint(); - } - } - - onPaint: { - var ctx = canvas.getContext('2d'); - ctx.save(); - - var image = ctx.getImageData(0, 0, canvas.width, canvas.height); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.drawImage(image, -drawWidth, 0, canvas.width, canvas.height) - ctx.fillStyle = 'green' - // draw a filles rectangle - var height = canvas.height * canvas.value - ctx.fillRect(canvas.width - drawWidth, canvas.height - height, - drawWidth, height) - ctx.restore() - } - } - - Text { - id: text - text: parent.text - anchors.topMargin: 2 - anchors.horizontalCenter: canvas.horizontalCenter - anchors.top: canvas.bottom - font.pointSize: 12; - } - - } - } - - Column { - id: column1 - x: 12; y: 12 - spacing: 24 - Row { - spacing: 16 - Loader { - sourceComponent: graphTemplate; - property string loadText: "Key Left" - property int control: ControllerIds.Hardware.Keyboard2.Left - } - Loader { - sourceComponent: graphTemplate; - property string loadText: "DPad Up" - property int control: ControllerIds.Hardware.X360Controller1.DPadUp - } - /* - Loader { - sourceComponent: graphTemplate; - property string loadText: "Yaw Left" - property int control: ControllerIds.Actions.YAW_LEFT - } - Loader { - sourceComponent: graphTemplate; - property string loadText: "Yaw Left" - property int control: ControllerIds.Actions.YAW_LEFT - } -*/ - -// Loader { sourceComponent: graphTemplate; } -// Loader { sourceComponent: graphTemplate; } -// Loader { sourceComponent: graphTemplate; } - } - /* - Row { - spacing: 16 - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - } - Row { - spacing: 16 - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - Loader { sourceComponent: graphTemplate; } - } - */ - - - Button { - text: "Go!" - onClicked: { - // - -// var newMapping = NewControllers.newMapping(); -// console.log("Mapping Object " + newMapping); -// var routeBuilder = newMapping.from("Hello"); -// console.log("Route Builder " + routeBuilder); -// routeBuilder.clamp(0, 1).clamp(0, 1).to("Goodbye"); - } - } - - Timer { - interval: 50; running: true; repeat: true - onTriggered: { - NewControllers.update(); - } - } - - } -} +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./xbox" +import "./controls" + +Column { + id: root + property var xbox: NewControllers.Hardware.X360Controller1 + property var actions: NewControllers.Actions + property var standard: NewControllers.Standard + property string mappingName: "TestMapping" + + spacing: 12 + + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + NewControllers.update(); + } + } + + Row { + spacing: 8 + Button { + text: "Default Mapping" + onClicked: { + var mapping = NewControllers.newMapping("Default"); + mapping.from(xbox.A).to(standard.A); + mapping.from(xbox.B).to(standard.B); + mapping.from(xbox.X).to(standard.X); + mapping.from(xbox.Y).to(standard.Y); + mapping.from(xbox.Up).to(standard.DU); + mapping.from(xbox.Down).to(standard.DD); + mapping.from(xbox.Left).to(standard.DL); + mapping.from(xbox.Right).to(standard.Right); + mapping.from(xbox.LB).to(standard.LB); + mapping.from(xbox.RB).to(standard.RB); + mapping.from(xbox.LS).to(standard.LS); + mapping.from(xbox.RS).to(standard.RS); + mapping.from(xbox.Start).to(standard.Start); + mapping.from(xbox.Back).to(standard.Back); + mapping.from(xbox.LY).to(standard.LY); + mapping.from(xbox.LX).to(standard.LX); + mapping.from(xbox.RY).to(standard.RY); + mapping.from(xbox.RX).to(standard.RX); + mapping.from(xbox.LT).to(standard.LT); + mapping.from(xbox.RT).to(standard.RT); + NewControllers.enableMapping("Default"); + } + } + + Button { + text: "Build Mapping" + onClicked: { + var mapping = NewControllers.newMapping(root.mappingName); + // Inverting a value + mapping.from(xbox.RY).invert().to(standard.RY); + // Assigning a value from a function + mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); + // Constrainting a value to -1, 0, or 1, with a deadzone + mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); + mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw); + } + } + + Button { + text: "Enable Mapping" + onClicked: NewControllers.enableMapping(root.mappingName) + } + + Button { + text: "Disable Mapping" + onClicked: NewControllers.disableMapping(root.mappingName) + } + } + + Row { + spacing: 8 + Xbox { device: root.xbox } + Xbox { device: root.standard } + } + + + Row { + ScrollingGraph { + controlId: NewControllers.Actions.Yaw + label: "Yaw" + min: -3.0 + max: 3.0 + size: 128 + } + } +} + diff --git a/tests/controllers/qml/controls/AnalogButton.qml b/tests/controllers/qml/controls/AnalogButton.qml new file mode 100644 index 0000000000..26f91458ac --- /dev/null +++ b/tests/controllers/qml/controls/AnalogButton.qml @@ -0,0 +1,45 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + property int size: 64 + width: size + height: size + property int controlId: 0 + property real value: 0 + property color color: 'black' + + function update() { + value = NewControllers.getValue(controlId); + canvas.requestPaint(); + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: root.update(); + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.beginPath(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + var fillHeight = root.value * canvas.height; + + ctx.fillStyle = 'red' + ctx.fillRect(0, canvas.height - fillHeight, canvas.width, fillHeight); + ctx.fill(); + ctx.restore() + } + } +} + + diff --git a/tests/controllers/qml/controls/AnalogStick.qml b/tests/controllers/qml/controls/AnalogStick.qml new file mode 100644 index 0000000000..8860aea49c --- /dev/null +++ b/tests/controllers/qml/controls/AnalogStick.qml @@ -0,0 +1,50 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + property int size: 64 + width: size + height: size + + property int halfSize: size / 2 + property var controlIds: [ 0, 0 ] + property vector2d value: Qt.vector2d(0, 0) + + function update() { + value = Qt.vector2d( + NewControllers.getValue(controlIds[0]), + NewControllers.getValue(controlIds[1]) + ); + canvas.requestPaint(); + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: root.update() + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.beginPath(); + ctx.clearRect(0, 0, width, height); + ctx.fill(); + ctx.translate(root.halfSize, root.halfSize) + ctx.lineWidth = 4 + ctx.strokeStyle = Qt.rgba(Math.max(Math.abs(value.x), Math.abs(value.y)), 0, 0, 1) + ctx.moveTo(0, 0).lineTo(root.value.x * root.halfSize, root.value.y * root.halfSize) + ctx.stroke() + ctx.restore() + } + } +} + + diff --git a/tests/controllers/qml/controls/ScrollingGraph.qml b/tests/controllers/qml/controls/ScrollingGraph.qml new file mode 100644 index 0000000000..69f919aaf1 --- /dev/null +++ b/tests/controllers/qml/controls/ScrollingGraph.qml @@ -0,0 +1,104 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + property int size: 64 + width: size + height: size + + property int controlId: 0 + property real value: 0.5 + property int scrollWidth: 1 + property real min: 0.0 + property real max: 1.0 + property bool log: false + property real range: max - min + property color color: 'blue' + property bool bar: false + property real lastHeight: -1 + property string label: "" + + function update() { + value = NewControllers.getValue(controlId); + canvas.requestPaint(); + } + + function drawHeight() { + if (value < min) { + return 0; + } + if (value > max) { + return height; + } + return ((value - min) / range) * height; + } + + Timer { + interval: 50; running: true; repeat: true + onTriggered: root.update() + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + Text { + anchors.top: parent.top + text: root.label + + } + + Text { + anchors.right: parent.right + anchors.top: parent.top + text: root.max + } + + Text { + anchors.right: parent.right + anchors.bottom: parent.bottom + text: root.min + } + + function scroll() { + var ctx = canvas.getContext('2d'); + var image = ctx.getImageData(0, 0, canvas.width, canvas.height); + ctx.beginPath(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height) + ctx.restore() + } + + onPaint: { + scroll(); + var ctx = canvas.getContext('2d'); + ctx.save(); + var currentHeight = root.drawHeight(); + if (root.lastHeight == -1) { + root.lastHeight = currentHeight + } + +// var x = canvas.width - root.drawWidth; +// var y = canvas.height - drawHeight; +// ctx.fillStyle = root.color +// ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1) +// ctx.fill(); +// ctx.restore() + + + ctx.beginPath(); + ctx.lineWidth = 1 + ctx.strokeStyle = root.color + ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) + ctx.stroke() + ctx.restore() + root.lastHeight = currentHeight + } + } +} + + diff --git a/tests/controllers/qml/controls/ToggleButton.qml b/tests/controllers/qml/controls/ToggleButton.qml new file mode 100644 index 0000000000..9ef54f5971 --- /dev/null +++ b/tests/controllers/qml/controls/ToggleButton.qml @@ -0,0 +1,43 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +Item { + id: root + width: size + height: size + property int size: 64 + property int controlId: 0 + property real value: 0 + property color color: 'black' + + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + root.value = NewControllers.getValue(root.controlId); + canvas.requestPaint(); + } + } + + Canvas { + id: canvas + anchors.fill: parent + antialiasing: false + + onPaint: { + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.beginPath(); + ctx.clearRect(0, 0, width, height); + if (root.value > 0.0) { + ctx.fillStyle = root.color + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + ctx.fill(); + ctx.restore() + } + } +} + + diff --git a/tests/controllers/qml/xbox/DPad.qml b/tests/controllers/qml/xbox/DPad.qml new file mode 100644 index 0000000000..8efe6c2b30 --- /dev/null +++ b/tests/controllers/qml/xbox/DPad.qml @@ -0,0 +1,43 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + + +Item { + id: root + property int size: 64 + width: size + height: size + property int spacer: size / 3 + property var device + property color color: 'black' + + ToggleButton { + controlId: device.Up + x: spacer + width: spacer; height: spacer + } + + ToggleButton { + controlId: device.Left + y: spacer + width: spacer; height: spacer + } + + ToggleButton { + controlId: device.Right + x: spacer * 2; y: spacer + width: spacer; height: spacer + } + + ToggleButton { + controlId: device.Down + x: spacer; y: spacer * 2 + width: spacer; height: spacer + } +} + + diff --git a/tests/controllers/qml/xbox/LeftAnalogStick.qml b/tests/controllers/qml/xbox/LeftAnalogStick.qml new file mode 100644 index 0000000000..ed2689e7c8 --- /dev/null +++ b/tests/controllers/qml/xbox/LeftAnalogStick.qml @@ -0,0 +1,21 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property int size: 64 + width: size + height: size + property var device + + AnalogStick { + size: size + controlIds: [ device.LX, device.LY ] + } +} + + diff --git a/tests/controllers/qml/xbox/RightAnalogStick.qml b/tests/controllers/qml/xbox/RightAnalogStick.qml new file mode 100644 index 0000000000..611b4d8f92 --- /dev/null +++ b/tests/controllers/qml/xbox/RightAnalogStick.qml @@ -0,0 +1,21 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property int size: 64 + width: size + height: size + property var device + + AnalogStick { + size: size + controlIds: [ device.RX, device.RY ] + } +} + + diff --git a/tests/controllers/qml/xbox/XboxButtons.qml b/tests/controllers/qml/xbox/XboxButtons.qml new file mode 100644 index 0000000000..4a9e87799e --- /dev/null +++ b/tests/controllers/qml/xbox/XboxButtons.qml @@ -0,0 +1,46 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property int size: 64 + width: size + height: size + property int spacer: size / 3 + property var device + property color color: 'black' + + ToggleButton { + controlId: device.Y + x: spacer + width: spacer; height: spacer + color: 'yellow' + } + + ToggleButton { + controlId: device.X + y: spacer + width: spacer; height: spacer + color: 'blue' + } + + ToggleButton { + controlId: device.B + x: spacer * 2; y: spacer + width: spacer; height: spacer + color: 'red' + } + + ToggleButton { + controlId: device.A + x: spacer; y: spacer * 2 + width: spacer; height: spacer + color: 'green' + } +} + + diff --git a/tests/controllers/qml/xbox/xbox360-controller-md.png b/tests/controllers/qml/xbox/xbox360-controller-md.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb596455fc3bb066c70cc1f2cf989a8ee05e0e9 GIT binary patch literal 29588 zcmXtwaF>^ZB^q^>x*W2xtj#aBzq;HB>-2IFI;naBybu@o;c( zzFB+F;o#ulIxFfZ;^5S$ytuV}ii3mm$Pc8hgi||Azm0=~!(^`k(!s$A;lRQ9@CgU! z8V3jG!#^CH_d+;0J2p5t(qD0KsC@ET4PI}! z-lQr9fR+Iy+V_v0FiR?aL?>h6h&mU5B?FkM!KT3hEuC;}K=HxB3I5VL0$IwWxB!Bu zKo~g;6c2ZNc61g?e<_`VW5q?uqUA?elI0)TJR`_ZDl$RkzR8+L*&mUY-QPDqk z!sFXwcpo1%qLPpfN_8_O6WgQP{w`v)Vzfj!<6oXfANEB7a8(T0C=*kMoZ|b|FYhA$ z`|m%g;A1v48f_llAAGzQbimUEET8ykA#iZj&g!Dlm zrFZXyvjnZn%F1HZ8N&fFO7&C9Y0e)t%O{c`5Me2)&-42nSp!AJRb$)!4O6vKZe!d2 z3%8e=7e*|pOsN{AiM<*ePzMp31{9xJO=emeIS(%{H!p9Ghs3hc#pEkqQpL^}NGJqZubd-rtOU6DMS7qE82u1E7sK-fx z-r#Mu7r2iMo+Jtjs|&cG0W6wl;Zg&PSjN(-Fxdkc}Qq6k=pqCr$w7Z?B0+`Q%^bzoYxn@m>iAc8zR6tc9m zES+#QGc$AX@L*#|nHU@EIy@9|Ysxu3_T3A(kxBg;`ZB)wGgrcf?`z){dX$H;M4xDO z(ygRT7o^;{YRpBfA<}(G;X(1w__kP){^#v&S0`uZ`uY7mRAXFOS=p}?_c)dWmUxxu zu`z>)s{!lrxw!-;e3cS|G~`BfhU2!}OWa2pK=vD@CNq9o2;Rr84@ie@wnViGsA3u|xB zkB@&&@Ztsbi<1t+4kD!!uEk?qM?n_nuNH14(&>xGBYPl{I}6+2*n9|L9=BViARW@Y zr8IwUn%Tg?1c>>e$w@#l*ZACAe@FPuca%rqFW1fq&GLz$b}J4}P8vqWzO9qhldHc@ zjbLNr;g>Yq>>P{oEf%^F&w+&*Lr$9I6Aiy%Anr6ITfB-AaYm}jkQc%b1d8ri{r3Gk zX}&J!vS5+^=Ys>U>zf;4S_r+g|G#AN+E=`$+1jzGsbtb`-e|%=d-I5t|Nc{xVIbKW z&bN1RiY+c?=^q$CQ@k+%ARR>htiMw#B8^h`5F|tVL9L|AMa)i!+Cja|xJnfcuI@>dEBf zJTByIGLb6O%@~@QB@8*`Ixjki&~&c~MRXRL z@~Lq^Q~6n4-|L$VX5X-+S^4|r@1|w2Clud`WUM_ArzMJJ8f>d$->oez{Zv-QK}$EJU#j2h+h?d`({%W7d*9x-_`HbczSv|y0oP0>dJkw z6e<|dmao9{J6qJ*M>=0jT-?CIg0Ig~M^~2|qQ=d~_h;3|Mu>U?F=t%R;Gh}PV_Q~M z_F1hYJu7S7M@pY3BavX{-#@M~m-Ng`N@7O2U##9g@;`7TT;4_0T6Yeg?apYjk*BAp z>w`cdBQ;ESbObRzEf#<5ga@4(e=UYAJEt*roy60N~q{ zo1@m^wm&&>9K5n}ZL~-k`=1v#SDU%sU$e7U|E99;EwzWL*MPi3yZX_-7Qcd0@9!g$ z8Rd07J^681Sy{u*QO*_M%HWR&M?qf;3d$-fKCi9Wp-?{;b^Q)>F#gg=v@h7$Sb(&o zrCVG|N`nJhR8s@FySdmhp_qAY05}Sg@%Q%^5)%5LR+8Gt$Y#>DK&2@~?-Ul6=PwQE zS^ZW~;i5PZPE_ zzxDj{x04^_r5D5H813!tXFVh;D=SA`#7>f}$)yTM`bI}dqnUjEjWU~=n+s=an}^fB z$@GyLygJ$1-rnxAtN)w4+oJpKr|><}fxLV|lM7J%^XJ#`@$uk@zcqFTm3~t6|LvZU z6T~>gg&7!-!NCb*V+Oyh1c!!(76O;UsUd0tmaQt0k@7V)HG9jQkzM|QZS^%zwE(Je zmn%*x0q%e*#xUoUt` zmXwrKfO*dM=hfZac_|awG3#c`Ef&f3^}-PkH_Olgw2O<&Z?~q$iRPMeg(+iW273W- zC@3h7C*9kGO@2jpG07FCq@|I2NYLS=YN%MP%7mP9JtL<7jk&;X<7tR&ip=rf=H>-N zlTC~g^HTVQWkJXe_i2bMW8|%{7b3Sk^ukVvx*1L6*M=YcVPwlbEX*QeedK-r$f7{* zkcg;g&+II9G1vIyq>7f-3m6O*sJ$>00!2D}&C4q-Dfw7mFU-TkQ&Cf6X=Cu{7`X87 zA6LC;Qceyn0Rch4U^r*|3@G#Z_ivo1PoL&Vy^HSdR+76rH3Sem<U{ zc$C)GCUrU8&Jknb{ zt3`3Z{py!CN6+D*kU&=A%|RPhqOf;PH(y%y?ORl4-WGpK=$jYQ|Ne0?MqDxnwB@U? zbQQ>kHph(Eh#rNsC#9qej9qJ8ZI`O8eC$#%GNPcMph(Zmw3Ep7c87;^ zenC2Du#sPe$(5Ct)6mh`J2~OSC@BU9zh-A=_f1*?*>9g*QPa?*_IdLDL`hN*#At9p zKNo*CtNG7G%&y67Z1*hD>@)8jap$t0wKZcvTfU1}f7Fv_Qel^enRzGtDS>ZZ7)M6R zM_y$`atn4YAxTgm{AdN1R2v6}Pud*HIyyv@iH@jTL)vl`7DXn~-&-eSgm~x=sN!N4 zf)_99P5EX$B;Nih?M-O$KFK}dfmsZ+EH^F)FYGK(+SmK#-n`kSZb0!N9YD3B?EDv@ z?N+Z9Osl3qrly}4y{x}Deg{lUOla%rO^l9ycQjPsrh4b$L5P^=H~F65MoUA8_dKrW zt2T$5yZa1%Ij2tevuDp*!mdP8WY4L~b@S&={jA37y-d0iR9J3zOgdTUiTyC`7fTs- z?98$bUI@DK3C(x!gwbd;Zw{^28mp0F2EohN9^00JEH*WekrBKsE4`l$ZC(cAe*MVe zdaoB0N15BPkd$tMp!Rz?2z$utFzjER+&!xR^Slo9^Lix$*B%{WxAbB^9tUuzYK+d# z{&b80835i%=MxeU8JL;%ulB^jtE=;!2xc@;@< z1*|)?#c0u(hr7;{F_(o?zfQEUmwFcv2D)8bzI)YW^CkS|d~u3`a*GB3@ljA-eEf5k zRD5x9@!9=z3cY7u!}00kilsn+U$3hGPX+-*O|(vJ)|mDA(!!t9mgk#(50=QE8ZfgY zHP)u4Ci6|sA++!5#l`wrNbu>HI(l3K(Y%d|&6|P1j+0kFAketUTRMO9bbEr3nArF7&=#eKm=k^; z*W(`$aJ<(z(jPT9ALGesT5p>5TXW_a5HyeHO2GPhcFIjWBO}9rR!sZ=_4*Nr*36Np_4Q7p!o-WJC1kuH$O>6Bp==sG(Paw zBOAHIC8K4I0gazz$vHg6a(kU3;+gjaC$}t@@z+zf#k`PC%+-m79dGRWRYCp z&dcN_?sFWIRTh7^xt?G zp5%P^e7JI>l2g#Pa0uwSwIVKyK46^3J7@C;@{rp6+3c7zT{t60&6?b$L zoNSF4T3ZXM_j`-a1HXrahQ{~W?YdcNdK+~vj|>hHMky!^OMw~(oiNkb}bJ=M(k*0b-rzt4+h+- zgYll-S3NxUnl`sJQ)Ouy)2zwTN3kM)~YsRr8LlC zP-H0|>A0Ln8376MfK z{QUD0=8>-{wGNDUGN=h-jUtCZ@_wCLn;3Hem`i;m)yv8Z0Tz$IsMhS3>17b$M~AM< zilyDKpwLvopM}H8hOJI3!RfgP=ExT?!W$Y*a>n3j(2YREgH=jvqZb85G-DMCiH*e*u==GT zM7QFf%)W@QF!K%DjD3>3o7>mCJO?*7H?2O`fu~wK z+s($y8BF*;7rpTC@Pc;W<+GiU53vcKb<5Y|ISXt?3S_a}EJW?3`0QC`g6BNE-YZvA zQ}cP3&6lI2BeWzymDEr2-ZI)>dZ@{LhD%V;I;+*Vn9ELxnv0KbV;rWn%TeiWt5yC9 z<&l}3OiWMQmlLKG&Gfg+bNT)p;p*y&N?$jIgJrJ&m3->4t+i!lbf5HmCvqIaoBDRVreeTdoy-qyF2EEn51MK zx3NkkW@&D&|7g8W@@&5b%!BpDs;UxISQHHm#+-;3%S2>lb=b(?>PwrLrJjYf|6KIy zUKPsL#;Y;rMZC?5I1OoE2pPg$3`C`YKxY=@@wk?jmMSc%ZIv&Yd+mxkI}5crSWOi3 zQ$ExNF-|WE+E&XXD>Z)4e3+aBFoqpIf3M3c{IC8^4hIXOAa8?7GMZJCS`Z{yRJSbs>x7o(n8j znBpVFCrLSk_VwMZ(qLnGw#EY>@RxoikxSIJm^|jf21R0p{qJ9V)u?(48iAqWIAyMOM;SQ{iW zJUIz4sk4H$x4&vz)HO7GNtqY}hhvR1(F!RX*O4ZcP+ksA%swv-pqec7}zV_K8{;A(Ku)=KJ z^6Z=(C4g!|LIO24H4`bm&&uyd=Leo8-+sp$m&8)Y@fPEIT@YG_pRt#e^z1DyvTW6I zX8?zwSuPpPJj*5)l%9K{`qp;1FTB!_Z1tu|2?+$K;tXl67tspA!B+`#qWQX_GBQb4b$woliLo(7P0i;d zvi{LkPZ-~oVLDeTz(pqD;si`dgM*-xCz0AmMM@#zXGbdZb>4W2Tg>!+F9~9}Y8nhd zzVcK8d7PAq2Il686Wb1Uc6K@uUt88A{0>$u9$9$vs$B1>uqetfa6*wdlE&%prC&+s zd#R4?`Hd*|H-ATY`1Yr#yRkZQYZ%!`ik9h+;-dPceO7*#oeMAcccs~>lm=g!;ROc- zWUj5Psfx}c5-i&S$RKJojEv+fT?u2#%nFrPb$!5v8X-$(lB3#9 z8_T)G7xi>0Y#eszZs7ss35tZOqHOjAfP4GNc`N6U`;y72spCZJ+h`97EoJ483t_9q zN5LT1O!t{u17l+~o(#LNu)M{#z?b;AT>Sj(l!?L;5?GgP)#s~&2y(S2ZpJZHg_ae* z6d{tGhtOc6cmb}OO3%tNw6f}`q{{h`uT*5(U^yR>*JJUh(SRE?9@`?CmOnH6%yP+qq@NqY|djh&VBl? zWwoj)kdaaE$_kTN&3Wdlj*>AhhLmTDq_gdzl29o0-&u&<&7LSN9o?9VSW-#~EB<44 zc6Jq(RPh;^&g|@Lnvl@YF&8nct$lqRvKP?CC$Q~L+qpb)c`22jpP!VRJif4?jr9-vDmyGF~n-64JM`&^Z=F-mfqnSp>$CcQ~vA)&&!qUe9X&JCFm(OY?$8vs12N9JL z11D!^B-%GUE6ZMtc6(>1+Y2#}Idv--J3da%z<-O`rWSC>iHrf#sk;xdQ1_9NDFFVW z!JpKL8_QR$za!3s2zqZZwUw1?@bZGS#lYoWr$z%y%b`DiaO+Kzii?YbrwBKbJq5Cq ztER}~RYrHu0000fAs)P{iWQ3dHnCk%TbpU}vvhlM-&lm2hQ>B5Ebj)>S@`i8i9il9 zhfxeaFYo$F*GH^bJY`}{MiArT<+apcr0<%sgt@R6qeY{D61k260h#(-pL*?5bB_c9 z?qM)kQc8-bgoK)*;Y1nzMHxZnWOO)Ysonja5DEHjR+Ah25214WTyyA+wxZ!Fpb`r@m0pQfPK4%`#X!yi2M@ul=0Eg4>P-t z4i32ax^caBischSyxk@s5KC5)E(bI{J^dS)$JNs_!#tY?qK2#Z+eHin2K$@aJ(aX8 zun?eHz8VfRUZ_wk8S}Y3wB_O93Bt5r{M+4~KaI#md1Mw9G3V)W^7Hd!C0>d?Ihcqy z4ap`~m~wF7h5BvE_tU&;YRyOL-_A~PQIW0TpFqQ>Rc&o_HO8Uyn?W20)}^nEdLEnzc2fKvrTx z0zv=405=a03xI%(Am%Wn{e_z>e9A2tE)PWxy8jUltYn4#UKlnFm0|d=uP^whm&&m| zp;y#m|0oT(Rly}bt{=J{lLOrPcW^>RT>gFD|A*K-H$OkRWvgJ_S^kE`(9jTUZ0yxP zzz;?G?9Yj(A=&CWu6c}%j1uM4&1#I#5&QFq6cYK{FT5tTJ}bYYf(7{u0Pu>6Z;g#F z>t@=xpWaL8e7CEo4i68XoSfX7Mek&+1+<~@3h5vl{^ud>07^;`S=phzJw71JS23+p zsfa0G*Ip#s#nm+%(b{Or&kJ`9mB?*}*`K*#Bu~p9_%|C!?R2BQ{Y_1#LH6!f@IkrK zS)!E`48oEJ3?JUa`8_0zkN1|JIW=Z0#}SW>jVZdiaudWnZZgv`Fd$P^RXrgf=!S5w zPEG<04Gn#_b{s@rdnb{{2k)z#JCf{kq#oKXJ_yJjWX}->}*1ow?{ZH zuh+ze&Ys?$M23BBAZMj|vv3>vPzenZ=^?T5w8b3IO;n|&ku!uHD#|_Fo;;mcgkbY; zFi&q)T?f$CmbnF-1{T7RB#KfhQFIhxtpKN|ryut7)*V(^-zB)u)YeBn+y@Ijf!lg| zdt>t1O<7r4OF?k)7?D9$;QW_N2;$|qGC4l3gwA&m9KewBtZh+OPmhE+?qim9M0Q$Q z+S6sh1eV6e#=JLy$$neI$f(!>OOo;lO+UYr2CW&xT7y{V1A#IUV5b|!wRxCARbRDI zsPFb>_-wn7wCR?J-I`9n|i+cme7j>^zGHjCne_K5ZS<9ZE1vs zK;PN!%=5S&Z0jHSD2E?|Rmd)Asgu(T^Xe%_rS#UB@Bqx$pi}N%y6ulC8vk z_K1DTEvta{F{-Oeac5^I=n9<$uc-LqUN>@Qz>|S(c1|bs{2uNu{LXfa){ZXI3JZZS z{1_#yCyDed9;0>+y?^Xz`-}ihRPTG1p`UYED5ZZB&NGm6IlUAa<$2D3Yftgnj@VF< zSf^*+g5mP{_T*YeX>{~s9v+^r+1YOc2xH^o;#F9F9sNi5qp1mbq$~MA4N*&GknPtJ zZ2vT}rCg-H63}LHx8~Pk0X8ua6M zuaqjM++5woei$E^i0qQ->(I7;CI zn5VZR{Ko5^760*^-Lue#k;svv&THYg`yI}``>h92H*@CI-9;6=5cE)PZtj>{lP`Y! zClI{!*RQ;Sf&!w#D~|-5v(wYR@4rm<9DkqRPl7;FHP|H94^94@o{9-%xljB7lHwMR zaRFiEbu(JZ$~`iE7UY#$Spum**x^;!$$wff(Ae~J>;T%VVLC*JI=zRL(mu=%KWnW{ zXx8hKMyb6tQ1s#R4au+1K-q_BP$lkpQ&^($L?_FttJLZ|&4+T!%&6>;CM zilg3Sy{ajYwYA{G()!Vt0W>x*YHxpqm~U73i3LHV^VO=R)-G;LDE`o-^kwBUnDf&X zNWYD<3S93sF!EZPjCTWQEl_bld?wWzcZ4qILx96ggtv$h9fZ~P*-=k0vsm9Rt z&+~_COv^Dz#|Zl2{Ko$wB~q7XrSIt9OVjgVM1N&f6}ooKZ*1HD>yT4ONQlIqh0RM` zcYl9>%e;N^Vy@zuU14fMi;l415|T(&HmGLD{k8j5R|Co;7u2KamMneIhKGlTWmRCX zn8mih<;<60H>oyiXhGE^`~AOBW`Y+lu6C;G@Z)+;nhmO^xBv+hdiJ=A3UxCt zs!jWq6P%U73zmP|fq zmUzwby-y=B&`I-@i&&}Gb-qe)@M~pd<&*z{+fn-1VEpv-RNunlS1rkRrJ`?TWwCx+ z!;1|Qf1R*pMr&*9#%%fc__z*tHs90mVMt45Wo2=7b<)z3)qj0`CXTWTgP`Y4aB%F{ z$~=CEB&u4&;TPmtz};)rUS3{t7X0sR_mA9S9v+!Qa<@Jfl%0@gp--sYxhxH8?2RBH zjaT^@aVPDC$Zc+Je!A8^S85xh6gB1M=B{a9j`xH zuix*7CMNfEr;u@TaWO%WLQ+ycZMPbVqR}T;J}nmMSy^>eC|=mp%ex4ZA1;;-`${^+^HHH8?j(Pe0;p>a>96S zAe=4)j%b*V%>RCEpAJm*sQ%1xB45(r|NcD^_bI(sXp-BMypneAw?p1qlKgyxjzCr} z6+BXuCj)rk#?PFoi`fz z$X#4q92-iS9YYW_SVnDYp~bJE$D> zZPvrF_2r5D!--DKz?0FLnLfS9Yr`~T>20istLs|?5NCk->bL>LN0}J2x@y~TwMoYi z@~u1JE$y`DSr~UhVj>MSwM|G!$kJLCSHkBOdhWuw z`^dY_+anlsAnM!iRtyt6XJI;`FhAeX*Ej9%=0fmVHci}FB>Nu*WBsnZY3jSJrC(WD z*+%JK$k#%xGuWb+r1QwqSK!_}!h3?2@~4Hs{ZQwXO3 zPtVQ_K%i)(gBiHG0fhzce%&0jxrouy($Qt-t8 zJIC`vJj3bq`s%T};HZJQV^L*yl3K~1{eY8Q>j&CKl(xP7dTbq+7V=P3RP;x4bN_Od z7TaG~+SkCQ;Vaj!xquI8eFdfGomUv4={w=AlSIJ|CIA6{=-_y}gq)nNyZdhIjj!Zj z_YMY=WgjH5vtKmk@)Of>^KXA2ogcW=Wg~=baXf;oG6&FW^0#ph3eL{ud|#rOd=6I> z`}_MP`1L?J(aA|kq=~&!L5ECtcXy}f=K`oTB(xt!Q%TYDUuaQ|F9W7h;!TEEG3e;KKTIefa-1}znpj8-C{m-@aJVOEn_qMm|H z>mrV!g;>4$=AX)h|7ZPOm>i?HxVV^@*owb28g|(PZqKM(yuaO&-?}_nr6$!o zDqYJc3_qs{Xv@#c%+wi>^z`!DTxjvb)|(~jDUWTIrj&LcPU|QSzUeb9_*=}>S(i69 zLb2(X;J@Cl<_mhj*JgSWf4URYO2UqMslY}?o*Dy}Eguc_^%Z0ooPB)0xX;v*CHA^_ zd3B(N8jY`SZmt3!4wjJ-ygygzv2M_IdEwHbMP+U(%Y$+eX_t+Dvk*%vKGGr0LxQ)D z*0J$zwK1=ekx|8zn_WHiQBa+bI6hbt9<;1zHckA*>n1TrYgA6Upr zwzgU=mB4#viQFO68e=h9BI3A@QBjYirKM*Zok{Mf2!ij>4|wencf8tWe3VwjUBr-S)KIhkW5#;foe(iV&QIEQa`I^Q~ep}syfBVrN)cDo& z56c&}B#_rPFj!xDb7;@4kzTG)FImIe%d4!YNHIABph{La(sy_$6doR43gQmfpHtP; zL{`=YOXOlrh&-8KU)08xQOZfk3u7kI6|6zU&(EJb_xvp>?)CNcC0a0&TM!QP{%zlv zeDwPXiN3XUO0QiD`nR(PO_&_xOs(a5qF~3$F?s;^aa8aGJ2&V$Rh?lqRh{9jA98E| z&%(RZGLeYR!p%)5={IkLbBBK29uLsb0^Z2T3<^dZy&!%it)i&-=;OzaTp7TcYfHqAS=tX59kohU6Zr;kD_?Lm7O!>tN zUcP*Z1E8Qd8j@W8?!7F>5PmvFRL`;KB`JS{8sXyShr!|T_4UHIieIW&7vk;1cuPa2 zXd!DSSBE!uY*0;9s~{KGZ!d&>y=mtASiuWaTBPCeohIeBS(}+=;r_)94L$wF*^IR{ z;!DYUifRjizBia(A3^Zaj*bXr(a|ir3M5if?*7s~BqYQku0d=+bIlEV%3DlKOk#(MauKPltgOXP zpGPHQ8t(4A()nHkeL6+@ql=4>A*XeCd4XBhh=oa4YtRvkNv#Ena(|t@s((MgZ+o1* z*0L?OZ#{eF_nPrN9#SRq@oh5wYb8U&mu^iV1KzPi3=0vv8@nwv;QrwqD??R=IW~LQqVf&{abzuNi4j5yGGUdB{9r1 zi%y4JpmSL;u=2fhzV9;|cuU16AWW%hs(5DiofQ4#%*+5pP@HxsQ`-+g3)i>d$*}YF zC6DW2R{u&uL}UO4|4C(4uc)i*^NwQdi|aG$9yn%Q;r^n`u;_8~i)#>vU4 zqO!8vuKtT|`3&2qH#7!-?Sli3Z0+NI-a37KeV-G`6Jw^QwjI40+8n_TcNa%NGFVL? zD4#%iWF}Gx9?#mR)M#P4-@oLmn;961y!SiDEO!=l6}!ug$yY~|PPnceNkw!PDlmQj zMxAY3)oWL8gP0f2*7nsNTI2$QQh%LnjXK5vnQqw|M(gi7e3bt)=J7s8ZEj8(jhT7_ z2%ehMS!MJjd1bwaE{}~Fh|vx;19&e>7=duGokVWEDWC5^eJ&!m?apn7ebH58-N?#H z*V&mfUga?qsiLg>L?A2i{yqXb=TcJxD=JbPHrd5s%B!jpwK-S?Z7QbR_$XgcP*Cj6 zBS_k<^msD5(Z0fO-Xsn=m6Vm?!9bcq)Ef&eegFW#!|bAH{{vWPr6Ba+tw`jt+pc-sX(cs^3e!8i?cU z)lcbv<*K?gZTP-+a(3qC<%Na^0%2+W{Xl{jFOGs_2#JZYM7}BC#BPTWBo|3YBlB8o* z4qGjFIzK%%g_Nb95%uwF(lC-IkH=+5*;3Qp@%%0jjlQ@!3X;(x_Akg!R4Z8>EmD55 z1Bx&HtXp2v+M44aqA5iE2Xi4RE*_&)^fe14ibxb&sAyzIivfi_{9-gq#zyDnc%F$Y z@&8=(s;sQc^w&+%v2yk}mZpThY8)Fb4gFHvQ8#ND>E9*A4Is$L$&re@|8@Hq#Un;b zq*juenwq-LY>E{U-orLDUcm9=^&CQk+rCe?WO4G+V*48=P9v=`pOCuaom#_rFSOMvMe?*|HxgkiDXN zN2I~R2vN(<%X6@^dyJUZVIxt$9TK=Fe%hw9du;Ok#o^A%uocUAs4-E!rU zF(Em*j;3h#sfiAA*0h;~|AkT5_%kq2a$;h_*RuQ>L(qrVSUe92I*DAOk})aokaTU1 z=eUn?A3su4S8oY9`Q*d zN=-c+WBB0zk~` z*xTDLwVFMOW?J!WxjY^aeAi-u^$@UGD%i-V2+R|&!s70hQu;so!Z%7PYoHes`4Bps z_gdN6nUh5B@*BNhW~Xj-e|Bf~I@(8ynug{rX`O%51@Gf7nw_e-priW8&%IzSMswZ4 zqcJM^;>L>?$&A6oB}*(1Itfs7Pl`B*!j6B&kkh*s3v*UefviLIs8J*5hP$dWkum`Y$Wr>y$2$XPwr~trG)@hO-)T8TKPLUt|Ak8ywCNi>wo|Khs~#62On<~i*La; z^QyvcGLeKf+uaJ5|v=EuA4XC+se=zSCo~Y|ExjbFYE*t&iRzW_# z(uxWVuXGmt$4CeAx|zh0Emms6G^E4ESK+qBj-u%V@aw>^uslpSCUkZ`z}GH0NJd+C zuxNr-ziLy`6p3#5Lr$h59H;B9QZxQVo8xM}^UfUS{9v(dD$8c+N=HX$^(-X#?rdfT z;8ySSKP-P}`Id;CojpB0eLRz-F7uadfKHZVgJ-uvpR3t31Xrn z&qwZ2{JlUo8y`RX9v~9!>&v+g6Ux^9(bV+r?)v?3p}d}@<)SCm%6zi6uI~8Qm|}Ri zEOz>PXlMwrSOxk&MCV|!?K$pa|A2tvF&7CHz@(cvaoor3Y#Qv(C31V68u^2XkAm_J z4!o8^F|tZUq|Zng(&?7HBujf?K{8mRuM%@6{?@d`A{nt9LDyuaV`*s_fea%~W8A<( zE@)rKjxy0I;*r9{{rkR7UsxJ)y zShvs3EuoJgG)Xq}+(Uy9B#>3o)|NX_u7_o#Y!`F=TIdZ84G9SeneZROU@)+;alhQ~ z|51WaD3p!-`OeNxMNQ4(^ivL4Hn!u8Jm!pS*x4a+Z}{H5Dzr76U((zxA^*zaQ(fnM zj#ieyk#pp^Gxj7_y-{Hy^Os)CIjJtCJuTA5Z@22)d*UFHT>*!$uBXUnnx}Zjt#r>T z0#rjnLa-BoQzq7yqaWVOlHo?BrIA{PE;3@$`EImAaeI4i$l0H*H5X|S5ipp+Re5U6PnQ%HMgadnBQ7+nprF8a$BQ&i)l&{OyL(N$2O@wD}Z*Smy6upFY zB^NN^T%N2hGk=L=>dv}_*gnKh@@vW2+uJi@J1ZyT&(T@*k>P_ve`gxax(RTLNH5&k%~x1$K{E({Sa$Z<`xmeqJdQ$DU9U{E-tTdC7qs%3_5=yu0x z!YHM#zCPllq@>`pX6J>U$l?829?MfsIYa6T55tP^goW zldY?(YyUt&pc^|ezC>1Be}BJ`QJPgsREj#6N0X(?YuaXNN&?00zz^;?d1kU3sCuTP zeo$wLii$GD82iY~roNmQ?kzX27~4EbA4X1{mcM(KkHOq79q<44?c3}3WK=N(*46$N z;#)a`U6D8T0$}Lx^#ccLD*DCw`Ol`Nri@N%jJZv(^4NZpeoI!Qd@t46L9r_Jlxu2DP0f?ACne0qa#~mWs}cV*zI#$*SHoak z?cZ~TLAT_Yf)G;a%z|xNXQKn9dzpy7Y=HxRi?_{7I z{DCs~151rL#!IdWrpcM2ty5w^fFDk~g|+L-%*{=;I9h57KiuU?>LA0wzTwCFmiGb=7vhB!Tk5USsas{F}e9{NLk%TDO4QivIp;>ZI=KerE_fFP?buyB+@OTlANEW(n%Zvp=o|h()NVFpnOn=J8kgEQ^%I)h!T#Z{>mRaHB$um z*Tgc08ggpgc#8unRX7wOG|kI@_^B#0G8mH)%gx3#A0FzJ89LRQBaujAqDYGnDHo8H zmy?S#$IN}F<%jDh$Ganu$O>{teIydO!s3{_oZ!>5xHsc9O?xwSa9OE((mgO@-A|bY zUS}@IlN6%|se6L^%deREXY=UP$bW0fwfI^078ICmipJ8ztc_p#$eB>NKGg(@`Owgi zEp}?(9i2r1r;O}+Hi6=1iW7suS|_6GkT6#A@CSHBV;{CkFG-^CTV{1kOePx+?!*@M zO;ng1+%l|qKm&J`xbyJg!^YO!MrM7!eji>nPj@b+IKYDt%OIVdot5RlIEcQ8j=mY) z{bS4TmFi*xoq#?K9~5e5XJ9Ko+LV;wrtHhlQEh2py2Wpc7y*{g)#(~BJozj@z#oCvMUU4}&ZAV95nVi>|nVBs5 zn_5L>*J-;o(rEE)X^l--Cmo*7~__$UJu!VF(iGa2_2X7U3=SLc3pU#7W zJ&V^hw6$;7n7*p25*k9@gTvu8^z@4!QV=1b)Q6eD@@xfqPUFGCg%GOv-o2olN;vE3yxqXu4`y;dm7!MB9xZIUjjrf zM^{%@&3H#w*H?M}&Q}2lL0`XSi|)D~J`&4VJ=s#p7GTzu7|c8!lJT^H5}vTKf*cqe z+zWuU{<}OYHYkHP8bMRMMmyt3z6~LB*?AFy}EhaPgyxJi;lN1p9;g!P{AmF#oz>ST91iSPfJ zc0WkUJGUQK(WI)dIbWb>ASGfA{rgCZXZ$tw#a4tt*(XCf3Fe`ZOFPuHlass9Gc2R(hNQDRV6S0}K01K~UGD>IuJqFxB0B8wKy8rDn|*C-u7 zKb`DYiB>qjTc8IBAlAWq@bZd^o%9>prQ`Y*7MfgC2^Ef(OBB2V zao#*hk6c|}ff(QUgv|vl7}W)O#P#OG1=ycuf|b~k3{}h< zozer!7MBM!@PWa>z{``>z||c8^z7^<`i{SZSS*z%H&vkUvTx` zXhR@S#zY|`AEb*r^-lO@^JJ_wA-#oqW; zlP{BuDTt^pTic+Z%;x51!Z7+z+^S~-Dg2hL0iM&o-)A+5BRc!~hgjw8vy$~h2;&? zce}1g;n8FnyxVo-@bK`>Fj6#Y7(B7C;~8&pNy)*GKi_{gKr^(chzab$bPwWm^U-Yk zEw~G^ywPZ-g8YVtRL9)F7pJGEEen}Y8j`{>2#}H5gEMMsMD1#Rw0hgPGOxt8vlBCr z5}{BiDW5e83gKA?8sUu!>`-4XIo6XY}P_}e3_2Tl1lIk_r4@`QsJ zf8HZ~RN-H?_v#2GSu`anl=3D^=q${Ax1EL zHMPI7u3iP{+|#upf2%2KMG6Xn@BrAz(h}G8>(^hsdiCqzWQ99I^Cr%~!K%#~%oO$Y z^#JN)X2xeC(3JJr9ZN|NZY!eh?k?zd`$pqYVg1^Fi@H-nlmi?`!(lGAhTuM zb7Wv;<;l_EB*sT#Gpt(t_Wmvc77|D4*UIPiVljiu3qUDvY028&c9YiCFEcbSGwW9- zm;2Nna`weZ+<&V+`8j{uU;?6qIJ%nv|61YLTIgmF@pXF_DPe=aZGu;-oS_|gmO_&5el_oG;h+Fx6)IB`qpnnB2o+_DV~A6QLZPWc zky-p&-kPGT{n+t@au2B-ou=f*PxxJlGI*&v>&iDYmtEUnm+FR`= z?{V*yo2jF#gS>L9{Yj3~`D0EizA}k~{Aq)LU4u#q++XU)ro03)7-tHizs9uI)&XnQ zH13@XzT$?D3`@U#qP^J7A1! zq<(w#((%r0s(u5P>R^`2mgo|y8A<5gg|QsI+fm_Ahx(2@4GAGM1@YQ1kJnTtM+nEa zO7?&%yW}}7ek@D=*}pBu>+yx~J)%aMaf| zADLIGMKgTxjYdch(OmiarJ0=pT(>__1bhAj>LvkxCv(>TuiOr-_|Il%t5DCFQ?d$& zB2UuY1odCXXE7=qwRvK6Xi9XCz2X28YmYUH=we^@$+Q&WZS+lRS37lGmt74#woIkE zT{akBhnx9l0!^t^*RYD@qI?J()g*^Oa!heVN&Q@sKfkT}q-*gEA@31IUW-x+H^-<} zOcaiL64x4jGJWmgJ(|S zvACTyS>@4@kwh(?<&YAI<3vKv3;tLRx5g|jo_o44C2EC!+8t*=Vrl5;22xY)FEkWL zq9QgnP=?Tn)(b(pTXblL+&7G*M5IL52?+^*Z28rh3;qWuHOHim+MLdvhjd)LA#mwR z7Ga88F(Gbb^u{C@LZfgyj!5K-vNFD4c{c0nG$--c(N#{Wn0(`kn*{g)FxDp2@87=# z$_!DtQ8lwq8JcIe9sbGRzt8=zkxLATHKS{Cl|Um(3jZ@H2&Hv9iQ6?!0|exUDOU*u z0wI#2^}|(S2#JLqCCkeS+gZe$V|WtO@xmx7wRn`FDIe`>mW031amE<(B;93-TkvX1 z&&&jQ+KdcFjs#-)y!Zgv$l;-QnIYnj?5yB-Pl+t6UD>jSe=IG9=slDJ4uj-~BEwxJ z(AL$Zm6fktCD0S79wfG4aVC2p-R@uWrg z*_PXV)l;A0b{vC)vkMCD^6>KR1;CznKSDY>-i+@4dvO^!vfDIs+peBz<#q2&&+`ZD zorj0w9^GTrS5+(;div{n#>Ov(kwx#`y*xb)%FN7!@bfEUj8`#L+N!aBY3CKtXDS?u z90`5Bwok5+L;=pjr%wd4v$LdRWD8zRyC=azLqosz0_<-|jh=GOp3QrtXJjxEDB3zX zy(}!`_%CXn-`1AX+${6+=g)T-47_@BRj^81C6fv%mT?ClFZ=ApP8ce+RSNXpAW9hi zTaXnL+=a1{-@A7Y@U=WW110G7X*9T~q;hoLzJ04#F#){b($f9)E|c%7t5?4iD2v~} zzZ~U~q*pOfJie|#Vhm$VON&2n6-ZRD_wh+SSREb5;o=9E?R; zAW94*3HX`GiVVuOGmYL3t5OiuP6@QNwE63f_Tt*fma7kq$VWL)>jEGW2ZP?pSWk)qDE za$m^Q{c~@x=as>K@!*7kjcs*zzMB~qpht%3!-lGN^(3e-y|9HB>7UrG61s#?UagfT zZNi3^2h%`W9gFquW`(RT{M_;*P|Pnc2XF+Tw4&Nt;D6BKp*F{)MnvF$L)|YkjLpbk z#1F^2xVTW^prnc+xPJY*J=QE4QNk=L91{I$YerZy?DbXZRm{g!)sklAEgvNy4t<&!$FL1UF|>ncIH3w9@5b|-Rkyt=Qi@1>c!S~2O%0NABk>DkPA6>Kw7si1RlZ?~T}@5}VGW>}a4Se#SL1hFqq zN;8wG=ogoj#eDpzghHXf0;#Uf@HM|!*6^B-3@K4JKK%n|%6~yt`_+!oRZeCy>MJax zmX1!LA(RrHpvBWYG(_6n-R*^V^Jv+ns$Bo;is z4i4OKINTItEKU7b$RItOCY}d#sGA4Bmlpiseq~!GYXWQ@jf~u#X zrytBC?E45_-kB=%9_b!ZTH;LHbm#VaMdEkiirU} zaf9d0J$7BL5`%%y3_4d3pUrCfQe!!_N(^crG+7D(wz5nPDLMH)85yWW%DsE{iVez) z6mAqk7LI}drqapHjfaBxmye9J=K{g<^76^*hC~LhX_;ZHlX&a|YIb9>ne&bDHCY*% zzYAz#1{Rj(uO*s|%N2=GCPQOmyBbqqH}LiK1q*m9D?k!v#>h#|`^$|uNZ(CI+sn;n zpYevgJ}lCMEaeR=6^-SOIYA&0a1%JyUiTTNkFVQ%c)Z!1tiC5A(s{Hw$;ise!xWd; zZ*P1gx9*dwnL-q*=B2jm^wxIXH0l^z?W|MQn8<(O^s zwyyjBjLS4biztN#E?B~{WOj!nD&hfqXK7w2LRXg@yqU?#t2YD%iShkftQxX2v$CAs z+}>g^alo}iLb5N%DT#NLGg|HpzYeZXjV&a(!lWAb@x1pI;LR;98M(RE6B{<-Ow0Sr zgC8t~49aM%tJB=W&Zd)7Q&&tVC!ZOVwNgf>^xO9>du__3e3+&n2>)N7<>}cOBO~J@ z4-bzXVZ1n1Wg{cn7Qd~+l9DLpXtEl|QP%Q`iW+l_>}RhgYP~W;pxRhlk0fxaefSTm z8`bJT6{BJtY~|gQ-KxZh?ja_?2gZ%sc#+Y8g!AHZ6^^^rUzg18fcNI6LJi%1sVMl5z9msy_WJ`5jUYymKV@~LMC6o*Fz&~_LxV3Yx?WyyVC(GDT z`J$qt$e5TI+Atj$>*_!X|31`3ih1Qn&Eu1Z>-;Je6Lat17vp_CkF53LT0Nz=3GOP< zOk1LTn__7A1qC*!dta2v_tuKyz0Uu7#>U3(FLn&;ml@X8)#+JTXV-nF9aLN0(Jp6g4$v*!(-4tZQt%Co9`hcmdb9_wg~z9s6V_ z3MiS1ii(#f7DO8FldC%}Pb~`cUM40cV&vaGiNE4X0hp$YtnB*Q+8giPRB(GtOnRwx zVO^Wm9U}*$3c9|&zM)L*%9I4xvW7oz&$a-Y-NDSq`Q2D$_VZAMDOb9yf`!+Bp>-V{ z2Qj{UUc3rNI5b0R#6{(eqvwihReW3=$Y%!2;{=b#*L`N5?zDl5M(f`N^zYdgNq3{! zyXjgy;9B8u!ikBA;KMPsY=yAUG1PmssJJ+n$+Ny?FW^!E!#l6rK~Dpk=NJeGy=rVB z&j*(ai;5n@?HnYsnvUn52CkQ+1)Q#z@(Bx%#vie~Fen4)HmNHYoN7^mW}49aQILC6 zH6-?Luspztu@e`MJLiU;Iz##S4{FRc^FbKrjPAjsM~|$h=oM+;GBPrM|1QP@w%Gpi zVC}e%G?bq|@N~U2@L*Una3#YLA|y0|o?v-3<%&Gso=r|mOVTL?MumoK_C>V1B zQ^~x?(!=EycI}(@GDrtj^TI9#6)E;3e5nm?S9q_Ec&-dFo}QgG`EC6a91c;Wcvo4u zH07ALJS>SzHVNOKIdKwaLb3M(tCMpC+OPU6hcUe7^si|zc^ z4Q9C@@tx}C!8Z#H8=ISy@Pr5KokK%IvAKeLQN6YzzxDzkQ0M@fPBK@m0~b~#^xb<@ z?l{%Dx)+IECPbguP0Y=W^z{*&R9m-|n3heTKqlRLRIc~9{aDU#XD-KvuvIewDNtWu zUp(&Ibj{7W?)!_n#PTsGz4qOYx7-=yRELlU#%zM*{3^g zV6?Peo^F2o_HF6HDDb^G#?aW9D!%t-l#&aTVD+S?nc0*TODXsA22devIq_=NloVtis(BFUm7APqxF%?L~$ma+u zQV_f?E#-qi9-MsD6kZN7o#`X`w?8Pn>};#1)*CBWu@m<140!&%R)4_)SL_<6`S|$m zGLu!8@4e(QXNn`r8&!kD3F*+wRlz#Eyu5r+s1le^+fxRYw^3bG@Pvca(Su=0Twk2F z3Dj8OZx?yMdP&-Lkb-h<2_?a`=x#+bGsZC|n$q#6jW;*y&9xykh2`ZNRhGWF+1afL zM!Od~VKzF$O%{X|SZorwQ>0{MRX@5yciN8kk1ywNi#_x<#Q>VEdv{bqje-CamxP3P zsA5F!->-*`W#;6-DF~DbD13c=*N#px@+I1wX@jDoqKc0na}I?4CI2^XA(V#x`t>VD zzDls5s3>y0RCgSQ>nQv}*zcG^UjeItQxFUc4%V0osBlnP3Qd-)@YA6m)tm2~1b_Sf zy>J56c$zE`%)%7ccK6H#z5`=F~Ru86dRa><*6iQV9rY~@iweEhr zb>g%Ht57X7-2W9xzW>vu+H-$NgPE*1_W0-|*s{ehuAty9z!6i$e6p%x%DIw<)3taE zRg3hu54{@MV*80a*GR+$qrdDhmp@gaX~^IC=vS{_i;0N5FVFw7 z6TB)Lc;jjAG_Z^PcpRUgQ@R(_mM!g#G%{i>mOv3jhA;NU<7!er%w2YH(3y z%lfwzeEgq26A$uvcSF#jUO9 zulLo^h3Xs$`OVEtcqyO){erfX^tX61q5!CYi;Ih(bPrE8GBTnal|K^zhASma&B3b^ zWPkjqWF2y3R68ZmWceCRFI;6>2j1ZQ_6tSv@`*0hvwVdqgUc^_>}vT{2_+N7+pKq3 z9Ib9fbgr#II!=b&jV*nzxzXUBZj_b&ojY#ty}UTvF^xv%Zoxf0MXOu)V+9QPX)&e( zpN}>t$58b^#RO1G8d};}%298gBtR3rs{(b-ud-@U6-sM)%&MbE!NA0{AlY$pJ2EoT zvOSowtYivs+4nzD>+XKO;||P$!7=HuEQFa^hO2Ye0Nf=wnE8WfRM5YRV=#&E2?@=O z6>@{3I>S{j3j9lUQ$j+*VuP|fZJRsKBD^Jg&3t`)D&INUIyi90D_lysx9$-g&-&Z6 zpLyj97L=DG8tRZpBm*p9^3oX!lslpMiQwkbR#Ky+5OdW`14U373f12-(UbcO* zY~kK^l&VxOKTBQ z=^`WDU%RohQ`*syr^4Ymc)^u70mP!c#onv07Hv#57N7$$C8I*x_+I~FCk@}3dUHYG zWGt<(EmAFy=FUizjj1rH_BuUWr!AfCWK*UG_jbGE-(~A4bhH6ZK>!xFdFTCkZw8kE zrZFQu-E^#dadGiwp}GK_G7TIoLzu~WU%wpR4}bw&{oF#@c`_XhP5$KO&FJp6qf?f9 zxUIFdU;Z6uEFmEw{B#xN<nN+l|yFbfE(vVMfz zqqEUU1;6?dIG4PnrRN6LeX5*n@wvx3f89H&^8c zGy$+&+Ios40YQv0fA!E4qvQuNIV-0}n*iOLoSZDsJdEFk|K3!9*@j|Slzvy@PalKZrQf?F9Abwju$8tv|JRB6}CR zoWuvL-tt4C65KlaR|uc$*RK>$w9GU&9jyRf)-X>Ra;lOwI8g+#{D_B_x5;aHpw^Ci zc%CifP#-+Td;5dJ56#Rn;%f%HCkyo;5Fw$feMNF|UsZ~SRBPo!D=I-}1YgW|Wb5Cj zK|$}z%Yk8hP`*WzB=xExf!Fc?Ey=B0^aLJHKC$;MdIFITq$g}_*wTco?EL)vrb7Pw zp6b3g3hQ>MZu?yJuylVm{b1?jI#Fc!+qXO$8yg}Fq{Rk&_~D(kNAqGCq~qh`TuB;K zF$C*AnF9j@xA}o(+O6?U2X1sRUO|X{!y=ZO@Rr*W{R^eLFJ8Q`FPVFJ{ly)POfadN zR9ONi!pMjYKw_Pl3D9k~?8pNjgEX&45&XVxZq5sXvf|20VAIyKwkDf-*&VA|H26Y+ zAX3qwjOM@YVh#?=&1aE>itGvMUnVAc_7-~q3-?HVK@eZKa0~(@eng4aEa#_^rp+er z)em0FB-Yhwb911BQkoCV#!=A5#)g#F67i_QKNzsvAhDQD&*gq^-o$?UwVj>Sab1OF zv~YVq7MrxUx5s^95y1|&!k|wZ2DALF?4cC%YhUyP6+B^NM2DH|hAwRHQ!MnYfXkz< zIQ6~6L@F6s*?uH;b7ho@Abfq@`D$Y=BLkrMR#sL+BO^jG?1&Po=c;#9~n_Gr<Ey`|cXr~<&d&b5 zxa5v1|L`GQ?&%+o!!>>2D%x&4o@=d!>gG;!Q4oWhDv1m3?m^S1)ql|9p$2@H+S){r zWs1B(UO2w~z4)5J{TFj{*12JqCq}-8HZ`_gam;z~@~6c0=Gxxg_n6|4+Baq1SO{JnSJrff(P0d?pf3_C)df9C1ai#@&zm9^M{I;0LKmY45qHYZ zryTP@V}K`51i87n-x-!$5S8-Lka#sQTUl8J@Ava+>FRpVe~b#WE76015@TIm^3R{< zWNh+77st5KuE-mnQp|w`14)k8V|wgB*QBYb{)q~c^{p)jA0L|1@&4bx1v0dV6e*lg zsKWaC6ng7`@P>v4+LY1+C`Kwr=W}nbwBK(Aa7{<|+E85)ckUjxwY6D(@CXlp>A>NH za5#MJ=rmCDP6Yd#!DaQU&R$JPpzRmEur(KJ{9rp@(U`io_r2SdE3!5F;xq&>m)HhV~YF1NZxRSLN~&v(-BWm%WM& z_*$)nlai7cNQr>%Z)G)`N&4pg%~TZuy2rM*&)VDNErsZr;)vR;g=KeYrz(TXQIB&G%el8KhV&R!d7u zL|VH4*DufzCjcfSyZc{%z&7jnS-0MK4?RmuEi*GlI2@j?^)?u)oeP`J_L0%%NML6s zyC)&>d1;BO%l3^r7f0z~nIV*v=sF!bYGJ{yyu92)ig}Z&_vqgZK`GH1=U>+Z@$m5I zeBh=&%3cnSe4O3g*bSYZIN*s&2#bmci;6w{TEU2ihsO;*VgJa-LFO^|m;;`ei0C~* zk^6!o;)WvPGU8G)Vv>R)k}@J9MAu$|?w9{}z!P_SXJp|2bHM%o4*1o5_YEE%9y2(@ xLwBT)|06F4JVkqVI|p`EXD -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include +// +// main.cpp +// tests/gpu-test/src +// +// 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 +// + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include #include #include - -#include -#include -#include -#include -#include -#include - -const QString& getQmlDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/"; - qDebug() << "Qml Path: " << dir; - } - return dir; -} - -class AppHook : public QQuickItem { - Q_OBJECT - -public: - AppHook() { - qDebug() << "Hook Created"; - } -}; - -using namespace Controllers; - - -QString sanatizeName(const QString& name) { - QString cleanName{ name }; - cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" }); - return cleanName; -} - -const QVariantMap& getInputMap() { - static std::once_flag once; - static QVariantMap map; - std::call_once(once, [&] { - { - QVariantMap hardwareMap; - // Controller.Hardware.* - auto devices = DependencyManager::get()->getDevices(); - for (const auto& deviceMapping : devices) { - auto device = deviceMapping.second.get(); - auto deviceName = sanatizeName(device->getName()); - auto deviceInputs = device->getAvailabeInputs(); - QVariantMap deviceMap; - for (const auto& inputMapping : deviceInputs) { - auto input = inputMapping.first; - auto inputName = sanatizeName(inputMapping.second); - deviceMap.insert(inputName, input.getID()); - } - hardwareMap.insert(deviceName, deviceMap); - } - map.insert("Hardware", hardwareMap); - } - - // Controller.Actions.* - { - QVariantMap actionMap; - auto actionNames = DependencyManager::get()->getActionNames(); - int actionNumber = 0; - for (const auto& actionName : actionNames) { - actionMap.insert(sanatizeName(actionName), actionNumber++); - } - map.insert("Actions", actionMap); - } - }); - return map; -} - -int main(int argc, char** argv) { - DependencyManager::set(); - PluginManager::getInstance()->getInputPlugins(); - - foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - QString name = inputPlugin->getName(); - auto userInputMapper = DependencyManager::get(); - if (name == KeyboardMouseDevice::NAME) { - auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky - keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); - keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper); - } - } - - // Register our component type with QML. - qmlRegisterType("com.highfidelity.test", 1, 0, "AppHook"); - //qmlRegisterType("com.highfidelity.test", 1, 0, "NewControllers"); - QGuiApplication app(argc, argv); + +#include +#include +#include +#include +#include +#include + +#include +#include + +const QString& getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/"; + qDebug() << "Qml Path: " << dir; + } + return dir; +} + +using namespace controller; + + +class PluginContainerProxy : public QObject, PluginContainer { + Q_OBJECT +public: + PluginContainerProxy() { + Plugin::setContainer(this); + } + virtual ~PluginContainerProxy() {} + virtual void addMenu(const QString& menuName) override {} + virtual void removeMenu(const QString& menuName) override {} + virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; } + virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {} + virtual bool isOptionChecked(const QString& name) override { return false; } + virtual void setIsOptionChecked(const QString& path, bool checked) override {} + virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {} + virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {} + virtual void showDisplayPluginsTools() override {} + virtual void requestReset() override {} + virtual QGLWidget* getPrimarySurface() override { return nullptr; } + virtual bool isForeground() override { return true; } + virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } +}; + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("NewControllers", new NewControllerScriptingInterface()); - engine.rootContext()->setContextProperty("ControllerIds", getInputMap()); - engine.load(getQmlDir() + "main.qml"); - app.exec(); - return 0; -} - - - -//QQmlEngine engine; -//QQmlComponent *component = new QQmlComponent(&engine); -// -//QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); -// -//component->loadUrl(QUrl("main.qml")); -// -//if (!component->isReady()) { -// qWarning("%s", qPrintable(component->errorString())); -// return -1; -//} -// -//QObject *topLevel = component->create(); -//QQuickWindow *window = qobject_cast(topLevel); -// -//QSurfaceFormat surfaceFormat = window->requestedFormat(); -//window->setFormat(surfaceFormat); -//window->show(); -// -//rc = app.exec(); -// -//delete component; -//return rc; -//} - - - -#include "main.moc" + + + { + DependencyManager::set(); + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + QString name = inputPlugin->getName(); + inputPlugin->activate(); + auto userInputMapper = DependencyManager::get(); + if (name == KeyboardMouseDevice::NAME) { + auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky + keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); + } + } + + + //new PluginContainerProxy(); + auto rootContext = engine.rootContext(); + auto controllers = new NewControllerScriptingInterface(); + rootContext->setContextProperty("NewControllers", controllers); + QVariantMap map; + map.insert("Hardware", controllers->property("Hardware")); + map.insert("Actions", controllers->property("Actions")); + rootContext->setContextProperty("ControllerIds", map); + } + engine.load(getQmlDir() + "main.qml"); + app.exec(); + return 0; +} + +#include "main.moc" + From ebeb87ba62d7c6b0b82e3fc6bfd69ccc42e38553 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Oct 2015 11:21:40 -0700 Subject: [PATCH 015/232] test --- libraries/input-plugins/src/input-plugins/StandardController.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/input-plugins/src/input-plugins/StandardController.h b/libraries/input-plugins/src/input-plugins/StandardController.h index fa660e15b8..7e0e163358 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.h +++ b/libraries/input-plugins/src/input-plugins/StandardController.h @@ -21,6 +21,7 @@ typedef std::shared_ptr StandardControllerPointer; +// small change class StandardController : public QObject, public InputDevice { Q_OBJECT Q_PROPERTY(QString name READ getName) From f860ca923ef84a47cd93c25daf4e5ed2d367e121 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Oct 2015 16:01:26 -0700 Subject: [PATCH 016/232] revert small hack --- libraries/input-plugins/src/input-plugins/StandardController.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/input-plugins/src/input-plugins/StandardController.h b/libraries/input-plugins/src/input-plugins/StandardController.h index 7e0e163358..fa660e15b8 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.h +++ b/libraries/input-plugins/src/input-plugins/StandardController.h @@ -21,7 +21,6 @@ typedef std::shared_ptr StandardControllerPointer; -// small change class StandardController : public QObject, public InputDevice { Q_OBJECT Q_PROPERTY(QString name READ getName) From 58d3578fb1e53c3f58165cde3637ec360f1de0bb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 12 Oct 2015 17:59:01 -0700 Subject: [PATCH 017/232] Making anonymous mappings work --- .../controllers/src/controllers/Mapping.cpp | 4 +++ .../controllers/src/controllers/Mapping.h | 3 ++ .../NewControllerScriptingInterface.cpp | 2 +- .../NewControllerScriptingInterface.h | 2 +- .../controllers/impl/MappingBuilderProxy.cpp | 5 ++++ .../controllers/impl/MappingBuilderProxy.h | 4 ++- tests/controllers/CMakeLists.txt | 2 +- tests/controllers/qml/content.qml | 30 ++++++++++++++++--- tests/controllers/src/main.cpp | 3 ++ 9 files changed, 47 insertions(+), 8 deletions(-) diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index 0063a1d24a..7e9ce5d1c4 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -1,5 +1,9 @@ #include "Mapping.h" namespace controller { + Mapping::Mapping(const QString& name ) : _name(name) { + + } + } diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 5b54a1745b..88d2492986 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -27,7 +27,10 @@ namespace controller { using Map = std::map; using Pointer = std::shared_ptr; + Mapping(const QString& name); + Map _channelMappings; + const QString _name; void parse(const QString& json); QString serialize(); diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index f5d6276b91..80089b1136 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -158,7 +158,7 @@ namespace controller { qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; } qDebug() << "Creating new Mapping " << mappingName; - Mapping::Pointer mapping = std::make_shared(); + auto mapping = std::make_shared(mappingName); _mappingsByName[mappingName] = mapping; return new MappingBuilderProxy(*this, mapping); } diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index 6bf0bda40d..659c8bfd05 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -39,7 +39,7 @@ namespace controller { Q_INVOKABLE float getValue(const int& source); Q_INVOKABLE void update(); - Q_INVOKABLE QObject* newMapping(const QString& mappingName); + Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 4e2c6a4d8c..a826b4fd4d 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -41,4 +41,9 @@ QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& sour return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } +QObject* MappingBuilderProxy::enable(bool enable) { + _parent.enableMapping(_mapping->_name, enable); + return this; +} + } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index b5e02bbfdf..70495a9cfe 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -30,8 +30,10 @@ public: Q_INVOKABLE QObject* from(const QJSValue& source); Q_INVOKABLE QObject* from(const QScriptValue& source); - Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); + Q_INVOKABLE QObject* enable(bool enable = true); + Q_INVOKABLE QObject* disable() { return enable(false); } + protected: QObject* from(const Endpoint::Pointer& source); diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index 34ab4c2eba..d9bef079ff 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME controllers-test) # This is not a testcase -- just set it up as a regular hifi project -setup_hifi_project(Script) +setup_hifi_project(Script Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index ce8a491419..aba40af007 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -11,7 +11,7 @@ Column { property var xbox: NewControllers.Hardware.X360Controller1 property var actions: NewControllers.Actions property var standard: NewControllers.Standard - property string mappingName: "TestMapping" + property var testMapping: null spacing: 12 @@ -55,7 +55,7 @@ Column { Button { text: "Build Mapping" onClicked: { - var mapping = NewControllers.newMapping(root.mappingName); + var mapping = NewControllers.newMapping(); // Inverting a value mapping.from(xbox.RY).invert().to(standard.RY); // Assigning a value from a function @@ -63,17 +63,22 @@ Column { // Constrainting a value to -1, 0, or 1, with a deadzone mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw); + mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); + mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); + testMapping = mapping; + enabled = false + text = "Built" } } Button { text: "Enable Mapping" - onClicked: NewControllers.enableMapping(root.mappingName) + onClicked: root.testMapping.enable() } Button { text: "Disable Mapping" - onClicked: NewControllers.disableMapping(root.mappingName) + onClicked: root.testMapping.disable() } } @@ -85,6 +90,7 @@ Column { Row { + spacing: 8 ScrollingGraph { controlId: NewControllers.Actions.Yaw label: "Yaw" @@ -92,6 +98,22 @@ Column { max: 3.0 size: 128 } + + ScrollingGraph { + controlId: NewControllers.Actions.YAW_LEFT + label: "Yaw Left" + min: -3.0 + max: 3.0 + size: 128 + } + + ScrollingGraph { + controlId: NewControllers.Actions.YAW_RIGHT + label: "Yaw Right" + min: -3.0 + max: 3.0 + size: 128 + } } } diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index d694bcae1d..7182862ff1 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -82,6 +82,9 @@ public: int main(int argc, char** argv) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; + for (auto path : qApp->libraryPaths()) { + qDebug() << path; + } { From 9a9bdbbc4462d78769f08ce2f58241e5a98b7b65 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Oct 2015 17:59:58 -0700 Subject: [PATCH 018/232] hack in mappings to interface --- .../example/scripts/controllerScriptingExamples.js | 14 ++++++++++++++ .../src/scripting/ControllerScriptingInterface.cpp | 12 ++++++++++++ .../src/scripting/ControllerScriptingInterface.h | 7 ++++++- .../src/input-plugins/InputPlugin.cpp | 2 +- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index 6db7b38705..e678ff26eb 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -12,6 +12,20 @@ // Assumes you only have the default keyboard connected + +var hydra = Controller.Hardware.Hydra2; +if (hydra !== undefined) { + print("-----------------------------------"); + var mapping = NewControllers.newMapping("Default"); + var standard = Controller.Standard; + print("standard:" + standard); + mapping.from(hydra.LeftButton1).to(standard.A); + mapping.from(hydra.LeftButton2).to(standard.B); + mapping.from(hydra.LeftButton3).to(standard.X); + NewControllers.enableMapping("Default"); + print("-----------------------------------"); +} + Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 9bdf8d1a4a..54aa72da6b 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include "Application.h" #include "devices/MotionTracker.h" #include "ControllerScriptingInterface.h" @@ -31,6 +33,11 @@ ControllerScriptingInterface::ControllerScriptingInterface() : } +ControllerScriptingInterface::~ControllerScriptingInterface() { + delete _newControllerScriptingInterface; +} + + static int actionMetaTypeId = qRegisterMetaType(); static int inputChannelMetaTypeId = qRegisterMetaType(); static int inputMetaTypeId = qRegisterMetaType(); @@ -121,6 +128,11 @@ void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); wireUpControllers(engine); + + // hack in the new controller scripting interface... + _newControllerScriptingInterface = new controller::NewControllerScriptingInterface(); + engine->registerGlobalObject("NewControllers", _newControllerScriptingInterface); + } void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index aa0526accb..dfe87043cd 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -18,6 +18,9 @@ #include class PalmData; +namespace controller { + class NewControllerScriptingInterface; +} class InputController : public AbstractInputController { Q_OBJECT @@ -55,7 +58,8 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface public: ControllerScriptingInterface(); - + ~ControllerScriptingInterface(); + virtual void registerControllerTypes(ScriptEngine* engine); void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } @@ -169,6 +173,7 @@ private: void wireUpControllers(ScriptEngine* engine); + controller::NewControllerScriptingInterface* _newControllerScriptingInterface = nullptr; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index b52dd3f658..2b16d905f5 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -22,7 +22,7 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), new SDL2Manager(), - //new SixenseManager(), + new SixenseManager(), //new ViveControllerManager(), nullptr }; From dc32d5ae8d7c491e63b568c4f47cb07d12a655b2 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 12 Oct 2015 18:03:21 -0700 Subject: [PATCH 019/232] Current status of my code for json --- .../controllers/src/controllers/Mapping.cpp | 10 +++- .../controllers/src/controllers/Mapping.h | 4 +- .../NewControllerScriptingInterface.cpp | 4 ++ .../NewControllerScriptingInterface.h | 2 + .../controllers/impl/MappingBuilderProxy.cpp | 50 ++++++++++++++++++- .../controllers/impl/MappingBuilderProxy.h | 12 +++++ .../src/controllers/impl/RouteBuilderProxy.h | 4 ++ .../src/input-plugins/UserInputMapper.cpp | 24 ++++++++- .../src/input-plugins/UserInputMapper.h | 5 +- tests/controllers/src/main.cpp | 4 +- 10 files changed, 111 insertions(+), 8 deletions(-) diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index 0063a1d24a..b6214a43fc 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -1,5 +1,11 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 +// #include "Mapping.h" -namespace controller { -} + diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index 5b54a1745b..39fe6ba788 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -28,9 +28,9 @@ namespace controller { using Pointer = std::shared_ptr; Map _channelMappings; + QString _name; - void parse(const QString& json); - QString serialize(); + protected: }; } diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index f5d6276b91..c4d1bb65c4 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -301,6 +301,10 @@ namespace controller { return Endpoint::Pointer(); } + UserInputMapper::Input NewControllerScriptingInterface::inputFor(const QString& inputName) { + return DependencyManager::get()->findDeviceInput(inputName); + } + Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { auto iterator = _endpoints.find(inputId); if (_endpoints.end() == iterator) { diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index 6bf0bda40d..39791eacb2 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -68,6 +68,8 @@ namespace controller { Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); + UserInputMapper::Input inputFor(const QString& inputName); + friend class MappingBuilderProxy; friend class RouteBuilderProxy; private: diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 4e2c6a4d8c..71a8a417fd 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -11,11 +11,14 @@ #include #include +#include +#include + #include "RouteBuilderProxy.h" #include "../NewControllerScriptingInterface.h" #include "../Logging.h" -namespace controller { +using namespace controller; QObject* MappingBuilderProxy::from(const QJSValue& source) { qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); @@ -41,4 +44,49 @@ QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& sour return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } + +const QString JSON_NAME = QStringLiteral("name"); +const QString JSON_CHANNELS = QStringLiteral("channels"); +const QString JSON_CHANNEL_FROM = QStringLiteral("from"); +const QString JSON_CHANNEL_TO = QStringLiteral("to"); +const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); + + +void MappingBuilderProxy::parse(const QJsonObject& json) { + _mapping->_name = json[JSON_NAME].toString(); + + _mapping->_channelMappings.clear(); + const auto& jsonChannels = json[JSON_CHANNELS].toArray(); + for (const auto& channelIt : jsonChannels) { + parseRoute(channelIt); + } +} + +void MappingBuilderProxy::parseRoute(const QJsonValue& json) { + if (json.isObject()) { + const auto& jsonChannel = json.toObject(); + + auto newRoute = from(jsonChannel[JSON_CHANNEL_FROM]); + if (newRoute) { + auto route = dynamic_cast(newRoute); + route->filters(jsonChannel[JSON_CHANNEL_FILTERS]); + route->to(jsonChannel[JSON_CHANNEL_TO]); + + return + } + } +} + +QObject* MappingBuilderProxy::from(const QJsonValue& json) { + if (json.isString()) { + return from(_parent.endpointFor(_parent.inputFor(json.toString()))); + } else if (json.isObject()) { + // Endpoint is defined as an object, we expect a js function then + return nullptr; + } +} + + +Filter::List MappingBuilderProxy::parseFilters(const QJsonValue& json) const { + return Filter::List(); } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index b5e02bbfdf..d0101b95a7 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -32,12 +32,24 @@ public: Q_INVOKABLE QObject* from(const QScriptValue& source); Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); + + // JSON route creation point + Q_INVOKABLE QObject* from(const QJsonValue& json); + + + void parse(const QJsonObject& json); + // void serialize(QJsonObject& json); + protected: QObject* from(const Endpoint::Pointer& source); friend class RouteBuilderProxy; NewControllerScriptingInterface& _parent; Mapping::Pointer _mapping; + + + void parseRoute(const QJsonValue& json); + }; } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 63cd106edb..a62a465700 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -40,6 +40,10 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToInteger(); Q_INVOKABLE QObject* constrainToPositiveInteger(); + // JSON route creation point + Q_INVOKABLE QObject* filters(const QJsonValue& json); + Q_INVOKABLE void to(const QJsonValue& json); + private: void to(const Endpoint::Pointer& destination); void addFilter(Filter::Lambda lambda); diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index c29acc09af..325dc5dfe7 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -64,7 +64,7 @@ void UserInputMapper::resetDevice(uint16 deviceID) { } } -int UserInputMapper::findDevice(QString name) { +int UserInputMapper::findDevice(QString name) const { for (auto device : _registeredDevices) { if (device.second->_name.split(" (")[0] == name) { return device.first; @@ -82,6 +82,28 @@ QVector UserInputMapper::getDeviceNames() { return result; } +UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { + + // Split the full input name as such: deviceName.inputName + auto names = inputName.split('.'); + + if (names.size() >= 2) { + // Get the device name: + auto deviceName = names[0]; + auto inputName = names[1]; + + int deviceID = findDevice(deviceName); + if (deviceID != Input::INVALID_DEVICE) { + // getAllInputsForDevice(deviceID); + } + + + } + + return Input(); +} + + bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { return addInputChannel(action, input, Input(), scale); diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index 304e74e8cc..b7b105df5e 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -140,9 +140,12 @@ public: QVector getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } void resetAllDeviceBindings(); void resetDevice(uint16 deviceID); - int findDevice(QString name); + int findDevice(QString name) const; QVector getDeviceNames(); + Input findDeviceInput(const QString& inputName) const; + + // Actions are the output channels of the Mapper, that's what the InputChannel map to // For now the Actions are hardcoded, this is bad, but we will fix that in the near future enum Action { diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index d694bcae1d..b42359e48a 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -83,7 +83,9 @@ int main(int argc, char** argv) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - + for (auto path : qApp->libraryPaths()) { + qDebug() << path; + } { DependencyManager::set(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { From 3ca3c635c01e7c4606859d44d9f79878d46a0a4c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 12 Oct 2015 18:40:47 -0700 Subject: [PATCH 020/232] Support functions in QScript contexts --- .../NewControllerScriptingInterface.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index 80089b1136..4bc5b9eeb5 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -59,6 +59,25 @@ namespace controller { QJSValue _callable; }; + class ScriptEndpoint : public Endpoint { + public: + ScriptEndpoint(const QScriptValue& callable) + : Endpoint(UserInputMapper::Input(-1)), _callable(callable) { + } + + virtual float value() { + float result = (float)_callable.call().toNumber(); + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(newValue) })); + } + + private: + QScriptValue _callable; + }; + class CompositeEndpoint : public Endpoint, Endpoint::Pair { public: CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) @@ -297,6 +316,11 @@ namespace controller { return endpointFor(UserInputMapper::Input(endpoint.toInt32())); } + if (endpoint.isFunction()) { + auto result = std::make_shared(endpoint); + return result; + } + qWarning() << "Unsupported input type " << endpoint.toString(); return Endpoint::Pointer(); } From 8a0540234cf0979ef7654ccf91962f68cdecf676 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 12 Oct 2015 18:47:59 -0700 Subject: [PATCH 021/232] Find the joystick controller dynamically --- tests/controllers/qml/content.qml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index aba40af007..41d623a389 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -8,10 +8,21 @@ import "./controls" Column { id: root - property var xbox: NewControllers.Hardware.X360Controller1 property var actions: NewControllers.Actions property var standard: NewControllers.Standard property var testMapping: null + property var xbox: null + + + Component.onCompleted: { + var patt = /^X360Controller/; + for (var prop in NewControllers.Hardware) { + if(patt.test(prop)) { + root.xbox = NewControllers.Hardware[prop] + break + } + } + } spacing: 12 @@ -49,6 +60,8 @@ Column { mapping.from(xbox.LT).to(standard.LT); mapping.from(xbox.RT).to(standard.RT); NewControllers.enableMapping("Default"); + enabled = false; + text = "Built" } } From 7f8f5f66c714bda8505f5787eaece6ffe88ad261 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 13 Oct 2015 10:01:01 -0700 Subject: [PATCH 022/232] Working on plugin active concept --- .../src/display-plugins/NullDisplayPlugin.cpp | 3 --- .../src/display-plugins/NullDisplayPlugin.h | 2 -- .../src/display-plugins/OpenGLDisplayPlugin.cpp | 4 ++-- .../src/display-plugins/OpenGLDisplayPlugin.h | 1 - .../src/input-plugins/KeyboardMouseDevice.h | 6 +++--- .../input-plugins/src/input-plugins/SDL2Manager.h | 2 -- .../src/input-plugins/SixenseManager.cpp | 2 ++ .../src/input-plugins/ViveControllerManager.cpp | 3 +++ libraries/plugins/src/plugins/Plugin.h | 14 ++++++++++++-- 9 files changed, 22 insertions(+), 15 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 1f8242f081..914f80d983 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -27,7 +27,4 @@ void NullDisplayPlugin::preRender() {} void NullDisplayPlugin::preDisplay() {} void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} void NullDisplayPlugin::finishFrame() {} - -void NullDisplayPlugin::activate() {} -void NullDisplayPlugin::deactivate() {} void NullDisplayPlugin::stop() {} diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index bb1ab2d97f..4f2cc77b8f 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -15,8 +15,6 @@ public: virtual ~NullDisplayPlugin() final {} virtual const QString & getName() const override; - void activate() override; - void deactivate() override; void stop() override; virtual glm::uvec2 getRecommendedRenderSize() const override; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 505dae824a..d93040690f 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -57,12 +57,12 @@ void OpenGLDisplayPlugin::customizeContext() { } void OpenGLDisplayPlugin::activate() { - _active = true; + DisplayPlugin::activate(); _timer.start(1); } void OpenGLDisplayPlugin::stop() { - _active = false; + DisplayPlugin::activate(); _timer.stop(); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 52715ebde7..4b0e03788a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -44,7 +44,6 @@ protected: mutable QTimer _timer; ProgramPtr _program; ShapeWrapperPtr _plane; - bool _active{ false }; bool _vsyncSupported{ false }; }; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index d96566e9d1..e8a6131387 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -13,6 +13,9 @@ #define hifi_KeyboardMouseDevice_h #include + +#include + #include "InputDevice.h" #include "InputPlugin.h" @@ -65,9 +68,6 @@ public: virtual bool isJointController() const override { return false; } const QString& getName() const override { return NAME; } - virtual void activate() override {}; - virtual void deactivate() override {}; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/libraries/input-plugins/src/input-plugins/SDL2Manager.h index 52d39597ef..23e3ee059f 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.h @@ -34,8 +34,6 @@ public: virtual void init() override; virtual void deinit() override; - virtual void activate() override {}; - virtual void deactivate() override {}; virtual void pluginFocusOutEvent() override; virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3950fdea43..e7a4feedd8 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -85,6 +85,7 @@ bool SixenseManager::isSupported() const { } void SixenseManager::activate() { + InputPlugin::activate(); #ifdef HAVE_SIXENSE _calibrationState = CALIBRATION_STATE_IDLE; _avatarPosition = DEFAULT_AVATAR_POSITION; @@ -125,6 +126,7 @@ void SixenseManager::activate() { } void SixenseManager::deactivate() { + InputPlugin::deactivate(); #ifdef HAVE_SIXENSE CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); CONTAINER->removeMenu(MENU_PATH); diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index bb8267b616..a3374cc1c0 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -79,6 +79,7 @@ bool ViveControllerManager::isSupported() const { } void ViveControllerManager::activate() { + InputPlugin::activate(); #ifdef Q_OS_WIN CONTAINER->addMenu(MENU_PATH); CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, @@ -143,6 +144,8 @@ void ViveControllerManager::activate() { } void ViveControllerManager::deactivate() { + InputPlugin::deactivate(); + #ifdef Q_OS_WIN CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); CONTAINER->removeMenu(MENU_PATH); diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h index 68e012b8e1..f53d309e97 100644 --- a/libraries/plugins/src/plugins/Plugin.h +++ b/libraries/plugins/src/plugins/Plugin.h @@ -33,9 +33,18 @@ public: virtual void deinit(); /// Called when a plugin is being activated for use. May be called multiple times. - virtual void activate() = 0; + virtual void activate() { + _active = true; + } + /// Called when a plugin is no longer being used. May be called multiple times. - virtual void deactivate() = 0; + virtual void deactivate() { + _active = false; + } + + virtual bool isActive() { + return _active; + } /** * Called by the application during it's idle phase. If the plugin needs to do @@ -48,6 +57,7 @@ public: virtual void loadSettings() {} protected: + bool _active{ false }; static PluginContainer* CONTAINER; static QString UNKNOWN_PLUGIN_ID; From 261384b4bb0e1244860926254c7b91bdfcab62f5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 13 Oct 2015 11:47:06 -0700 Subject: [PATCH 023/232] Fixing build breakage --- .../input-plugins/src/input-plugins/KeyboardMouseDevice.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 202a767244..4703d3ae6a 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -10,9 +10,9 @@ // #include "KeyboardMouseDevice.h" -#include -#include -#include +#include +#include +#include const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; From cb62527bf93a1b32dcce94095a38583f163b4412 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Oct 2015 09:22:30 -0700 Subject: [PATCH 024/232] Refactoring the filter for supporting the factory --- .../controllers/src/controllers/Filter.cpp | 63 ++++++++- .../controllers/src/controllers/Filter.h | 132 ++++++++++++++++-- .../controllers/impl/MappingBuilderProxy.cpp | 9 +- .../controllers/impl/MappingBuilderProxy.h | 1 + .../controllers/impl/RouteBuilderProxy.cpp | 64 +++++---- .../src/controllers/impl/RouteBuilderProxy.h | 1 + 6 files changed, 218 insertions(+), 52 deletions(-) diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index 17715eceff..b7175f1716 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -11,11 +11,66 @@ #include #include -namespace controller { +#include +#include - Filter::Pointer Filter::parse(const QJsonObject& json) { - // FIXME parse the json object and determine the instance type to create - return Filter::Pointer(); +#include "SharedUtil.h" + +using namespace controller; + + +const QString JSON_FILTER_TYPE = QStringLiteral("type"); +const QString JSON_FILTER_PARAMS = QStringLiteral("params"); + +Filter::Factory Filter::_factory; + +Filter::Pointer Filter::parse(const QJsonObject& json) { + // The filter is an object, now let s check for type and potential arguments + Filter::Pointer filter; + auto filterType = json[JSON_FILTER_TYPE]; + if (filterType.isString()) { + filter.reset(Filter::getFactory().create(filterType.toString().toStdString())); + if (filter) { + // Filter is defined, need to read the parameters and validate + auto parameters = json[JSON_FILTER_PARAMS]; + if (parameters.isArray()) { + if (filter->parseParameters(parameters.toArray())) { + } + } + + return filter; + } } + return Filter::Pointer(); } +Filter::Factory::ClassEntry ScaleFilter::_factoryEntry; + +bool ScaleFilter::parseParameters(const QJsonArray& parameters) { + if (parameters.size() > 1) { + _scale = parameters[0].toDouble(); + } + return true; +} + +Filter::Factory::ClassEntry PulseFilter::_factoryEntry; + + +float PulseFilter::apply(float value) const { + float result = 0.0; + + if (0.0 != value) { + float now = secTimestampNow(); + float delta = now - _lastEmitTime; + if (delta >= _interval) { + _lastEmitTime = now; + result = value; + } + } + + return result; +} + +bool PulseFilter::parseParameters(const QJsonArray& parameters) { + return false; +} diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index f3978e2c0a..425bba7512 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -14,13 +14,49 @@ #include #include #include +#include #include class QJsonObject; +class QJsonArray; + namespace controller { + /* + template class Factory { + public: + template class Entry { + public: + virtual T* create() = 0; + }; + + template class DefaultEntry{ + public: + T* create() { return new S(); } + }; + + using EntryMap = std::map>>; + + void registerEntry(const std::string& name, std::unique_ptr>& entry) { + if (entry) { + _entries[name] = entry; + } + } + + T* create(const std::string& name) const { + auto& entryIt = _entries.find(name); + if (entryIt != _entries.end()) { + return (*entryIt).second->create(); + } + return nullptr; + } + + protected: + EntryMap _entries; + }; + */ // Encapsulates part of a filter chain class Filter { public: @@ -30,18 +66,71 @@ namespace controller { using List = std::list; using Lambda = std::function; - static Filter::Pointer parse(const QJsonObject& json); - }; + // Factory features + virtual bool parseParameters(const QJsonArray& parameters) = 0; + class Factory { + public: + + class Entry { + public: + virtual Filter* create() = 0; + + Entry() = default; + virtual ~Entry() = default; + }; + + template class ClassEntry { + public: + virtual Filter* create() { return (Filter*) new T(); } + + ClassEntry() = default; + virtual ~ClassEntry() = default; + }; + + using EntryMap = std::map>; + + void registerEntry(const std::string& name, const std::shared_ptr& entry) { + if (entry) { + _entries.insert(EntryMap::value_type(name, entry)); + } + } + + Filter* create(const std::string& name) const { + auto& entryIt = _entries.find(name); + if (entryIt != _entries.end()) { + return (*entryIt).second->create(); + } + return nullptr; + } + + protected: + EntryMap _entries; + }; + + static Filter::Pointer parse(const QJsonObject& json); + static Factory& getFactory() { return _factory; } + protected: + static Factory _factory; + }; +} + +#define REGISTER_FILTER_CLASS(classEntry) static Filter::Factory::ClassEntry _factoryEntry; + +namespace controller { class LambdaFilter : public Filter { public: + // LambdaFilter() {} LambdaFilter(Lambda f) : _function(f) {}; virtual float apply(float value) const { return _function(value); } + virtual bool parseParameters(const QJsonArray& parameters) { return true; } + +// REGISTER_FILTER_CLASS(LambdaFilter); private: Lambda _function; }; @@ -50,17 +139,21 @@ namespace controller { public: }; + + class ScaleFilter : public Filter { + public: + ScaleFilter() {} + ScaleFilter(float scale): _scale(scale) {} - //class ScaleFilter : public Filter { - //public: - // ScaleFilter(float scale); - // virtual float apply(float scale) const override { - // return value * _scale; - // } + virtual float apply(float value) const override { + return value * _scale; + } + virtual bool parseParameters(const QJsonArray& parameters); - //private: - // const float _scale; - //}; + REGISTER_FILTER_CLASS(ScaleFilter); + private: + float _scale = 1.0f; + }; //class AbstractRangeFilter : public Filter { //public: @@ -84,6 +177,23 @@ namespace controller { // const float _interval; //}; + + class PulseFilter : public Filter { + public: + REGISTER_FILTER_CLASS(PulseFilter); + PulseFilter() {} + PulseFilter(float interval) : _interval(interval) {} + + + virtual float apply(float value) const override; + + virtual bool parseParameters(const QJsonArray& parameters); + + private: + mutable float _lastEmitTime{ -std::numeric_limits::max() }; + float _interval = 1.0f; + }; + ////class DeadzoneFilter : public AbstractRangeFilter { ////public: //// DeadzoneFilter(float min, float max = 1.0f); diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 71a8a417fd..9080555cac 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -11,8 +11,8 @@ #include #include -#include -#include +#include +#include #include "RouteBuilderProxy.h" #include "../NewControllerScriptingInterface.h" @@ -71,8 +71,6 @@ void MappingBuilderProxy::parseRoute(const QJsonValue& json) { auto route = dynamic_cast(newRoute); route->filters(jsonChannel[JSON_CHANNEL_FILTERS]); route->to(jsonChannel[JSON_CHANNEL_TO]); - - return } } } @@ -87,6 +85,3 @@ QObject* MappingBuilderProxy::from(const QJsonValue& json) { } -Filter::List MappingBuilderProxy::parseFilters(const QJsonValue& json) const { - return Filter::List(); -} diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index d0101b95a7..799fc99399 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -17,6 +17,7 @@ class QJSValue; class QScriptValue; +class QJsonValue; namespace controller { diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index e6b67e9ca6..d606b52608 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -9,6 +9,9 @@ #include +#include +#include + #include #include "MappingBuilderProxy.h" @@ -61,9 +64,7 @@ QObject* RouteBuilderProxy::clamp(float min, float max) { } QObject* RouteBuilderProxy::scale(float multiplier) { - addFilter([=](float value) { - return value * multiplier; - }); + addFilter(Filter::Pointer(new ScaleFilter(multiplier))); return this; } @@ -99,39 +100,12 @@ QObject* RouteBuilderProxy::constrainToPositiveInteger() { } -class PulseFilter : public Filter { -public: - PulseFilter(float interval) : _interval(interval) {} - - virtual float apply(float value) const override { - float result = 0.0; - - if (0.0 != value) { - float now = secTimestampNow(); - float delta = now - _lastEmitTime; - if (delta >= _interval) { - _lastEmitTime = now; - result = value; - } - } - - return result; - } - -private: - mutable float _lastEmitTime{ -std::numeric_limits::max() }; - const float _interval; -}; - - QObject* RouteBuilderProxy::pulse(float interval) { Filter::Pointer filter = std::make_shared(interval); addFilter(filter); return this; } - - void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda); addFilter(filterPointer); @@ -141,4 +115,34 @@ void RouteBuilderProxy::addFilter(Filter::Pointer filter) { _route->_filters.push_back(filter); } + +QObject* RouteBuilderProxy::filters(const QJsonValue& json) { + // We expect an array of objects to define the filters + if (json.isArray()) { + const auto& jsonFilters = json.toArray(); + for (auto jsonFilter : jsonFilters) { + if (jsonFilter.isObject()) { + // The filter is an object, now let s check for type and potential arguments + Filter::Pointer filter = Filter::parse(jsonFilter.toObject()); + if (filter) { + addFilter(filter); + } + } + } + } + + return this; +} + +void RouteBuilderProxy::to(const QJsonValue& json) { + if (json.isString()) { + + return to(_parent.endpointFor(_parent.inputFor(json.toString()))); + } else if (json.isObject()) { + // Endpoint is defined as an object, we expect a js function then + return to(nullptr); + } + +} + } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index a62a465700..573e841e85 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -16,6 +16,7 @@ class QJSValue; class QScriptValue; +class QJsonValue; namespace controller { From 1302b6a2388d466064363067ec428166a1488670 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Oct 2015 10:47:49 -0700 Subject: [PATCH 025/232] Improving the Factory registration --- .../controllers/src/controllers/Filter.h | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index 425bba7512..4d21966731 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -75,24 +75,30 @@ namespace controller { class Entry { public: virtual Filter* create() = 0; - + virtual const std::string& getName() const = 0; + Entry() = default; virtual ~Entry() = default; }; - template class ClassEntry { + template class ClassEntry { public: - virtual Filter* create() { return (Filter*) new T(); } + Filter* create() override { return (Filter*) new T(); } + const std::string& getName() const override { + return _name + }; - ClassEntry() = default; + ClassEntry() : _name(name) {}; virtual ~ClassEntry() = default; + + const std::string _name; }; using EntryMap = std::map>; - void registerEntry(const std::string& name, const std::shared_ptr& entry) { + void registerEntry(const std::shared_ptr& entry) { if (entry) { - _entries.insert(EntryMap::value_type(name, entry)); + _entries.insert(EntryMap::value_type(entry->getName(), entry)); } } @@ -115,7 +121,9 @@ namespace controller { }; } -#define REGISTER_FILTER_CLASS(classEntry) static Filter::Factory::ClassEntry _factoryEntry; +#define REGISTER_FILTER_CLASS(classEntry, className) \ + using FactoryEntry = Filter::Factory::ClassEntry;\ + static FactoryEntry _factoryEntry; namespace controller { @@ -150,7 +158,8 @@ namespace controller { } virtual bool parseParameters(const QJsonArray& parameters); - REGISTER_FILTER_CLASS(ScaleFilter); + // static Filter::Factory::ClassEntry _factoryEntry; + REGISTER_FILTER_CLASS(ScaleFilter, "scale"); private: float _scale = 1.0f; }; From 619fce0d7fa5720b863c62c86e0878d93d11664a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 13:40:22 -0700 Subject: [PATCH 026/232] Fixing namespace usage in input-plugins --- .../src/input-plugins/Joystick.cpp | 334 +++++++++--------- .../src/input-plugins/Joystick.h | 146 ++++---- .../src/input-plugins/StandardController.cpp | 242 ++++++------- .../src/input-plugins/StandardController.h | 96 ++--- .../src/input-plugins/StandardControls.h | 114 +++--- 5 files changed, 468 insertions(+), 464 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index 5c6f43c604..684b9e80d5 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -1,167 +1,167 @@ -// -// Joystick.cpp -// input-plugins/src/input-plugins -// -// Created by Stephen Birarda on 2014-09-23. -// Copyright 2014 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 -// - -#include - -#include - -#include "Joystick.h" - -#include "StandardControls.h" - -const float CONTROLLER_THRESHOLD = 0.3f; - -#ifdef HAVE_SDL2 -const float MAX_AXIS = 32768.0f; - -Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : - InputDevice(name), - _sdlGameController(sdlGameController), - _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), - _instanceId(instanceId) -{ - -} - -#endif - -Joystick::~Joystick() { - closeJoystick(); -} - -void Joystick::closeJoystick() { -#ifdef HAVE_SDL2 - SDL_GameControllerClose(_sdlGameController); -#endif -} - -void Joystick::update(float deltaTime, bool jointsCaptured) { - for (auto axisState : _axisStateMap) { - if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { - _axisStateMap[axisState.first] = 0.0f; - } - } -} - -void Joystick::focusOutEvent() { - _axisStateMap.clear(); - _buttonPressedMap.clear(); -}; - -#ifdef HAVE_SDL2 - -void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { - SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; - _axisStateMap[makeInput((Controllers::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; -} - -void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { - auto input = makeInput((Controllers::StandardButtonChannel)event.button); - bool newValue = event.state == SDL_PRESSED; - if (newValue) { - _buttonPressedMap.insert(input.getChannel()); - } else { - _buttonPressedMap.erase(input.getChannel()); - } -} - -#endif - - -void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - // Buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y")); - - // DPad - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR")); - - // Bumpers - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB")); - - // Stick press - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS")); - - // Center buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back")); - - // Analog sticks - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX")); - - // Triggers - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT")); - - // Aliases, PlayStation style names - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right")); - - return availableInputs; - }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerDevice(_deviceID, proxy); -} - -void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { -#ifdef HAVE_SDL2 - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - -#endif -} - -UserInputMapper::Input Joystick::makeInput(Controllers::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input Joystick::makeInput(Controllers::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} - +// +// Joystick.cpp +// input-plugins/src/input-plugins +// +// Created by Stephen Birarda on 2014-09-23. +// Copyright 2014 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 +// + +#include + +#include + +#include "Joystick.h" + +#include "StandardControls.h" + +const float CONTROLLER_THRESHOLD = 0.3f; + +#ifdef HAVE_SDL2 +const float MAX_AXIS = 32768.0f; + +Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : + InputDevice(name), + _sdlGameController(sdlGameController), + _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), + _instanceId(instanceId) +{ + +} + +#endif + +Joystick::~Joystick() { + closeJoystick(); +} + +void Joystick::closeJoystick() { +#ifdef HAVE_SDL2 + SDL_GameControllerClose(_sdlGameController); +#endif +} + +void Joystick::update(float deltaTime, bool jointsCaptured) { + for (auto axisState : _axisStateMap) { + if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { + _axisStateMap[axisState.first] = 0.0f; + } + } +} + +void Joystick::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + +#ifdef HAVE_SDL2 + +void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { + SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; + _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; +} + +void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { + auto input = makeInput((controller::StandardButtonChannel)event.button); + bool newValue = event.state == SDL_PRESSED; + if (newValue) { + _buttonPressedMap.insert(input.getChannel()); + } else { + _buttonPressedMap.erase(input.getChannel()); + } +} + +#endif + + +void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getFreeDeviceID(); + + auto proxy = std::make_shared(_name); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); + + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + mapper.registerDevice(_deviceID, proxy); +} + +void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { +#ifdef HAVE_SDL2 + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float DPAD_MOVE_SPEED = 0.5f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BOOM_SPEED = 0.1f; + +#endif +} + +UserInputMapper::Input Joystick::makeInput(controller::StandardButtonChannel button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input Joystick::makeInput(controller::StandardAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index 70949a8b83..2a7a11d230 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -1,73 +1,73 @@ -// -// Joystick.h -// input-plugins/src/input-plugins -// -// Created by Stephen Birarda on 2014-09-23. -// Copyright 2014 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 -// - -#ifndef hifi_Joystick_h -#define hifi_Joystick_h - -#include -#include - -#ifdef HAVE_SDL2 -#include -#undef main -#endif - -#include "InputDevice.h" -#include "StandardControls.h" - -class Joystick : public QObject, public InputDevice { - Q_OBJECT - Q_PROPERTY(QString name READ getName) - -#ifdef HAVE_SDL2 - Q_PROPERTY(int instanceId READ getInstanceId) -#endif - -public: - - const QString& getName() const { return _name; } - - // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - - Joystick() : InputDevice("Joystick") {} - ~Joystick(); - - UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button); - UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis); - -#ifdef HAVE_SDL2 - Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); -#endif - - void closeJoystick(); - -#ifdef HAVE_SDL2 - void handleAxisEvent(const SDL_ControllerAxisEvent& event); - void handleButtonEvent(const SDL_ControllerButtonEvent& event); -#endif - -#ifdef HAVE_SDL2 - int getInstanceId() const { return _instanceId; } -#endif - -private: -#ifdef HAVE_SDL2 - SDL_GameController* _sdlGameController; - SDL_Joystick* _sdlJoystick; - SDL_JoystickID _instanceId; -#endif -}; - -#endif // hifi_Joystick_h +// +// Joystick.h +// input-plugins/src/input-plugins +// +// Created by Stephen Birarda on 2014-09-23. +// Copyright 2014 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 +// + +#ifndef hifi_Joystick_h +#define hifi_Joystick_h + +#include +#include + +#ifdef HAVE_SDL2 +#include +#undef main +#endif + +#include "InputDevice.h" +#include "StandardControls.h" + +class Joystick : public QObject, public InputDevice { + Q_OBJECT + Q_PROPERTY(QString name READ getName) + +#ifdef HAVE_SDL2 + Q_PROPERTY(int instanceId READ getInstanceId) +#endif + +public: + + const QString& getName() const { return _name; } + + // Device functions + virtual void registerToUserInputMapper(UserInputMapper& mapper) override; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + Joystick() : InputDevice("Joystick") {} + ~Joystick(); + + UserInputMapper::Input makeInput(controller::StandardButtonChannel button); + UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); + +#ifdef HAVE_SDL2 + Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); +#endif + + void closeJoystick(); + +#ifdef HAVE_SDL2 + void handleAxisEvent(const SDL_ControllerAxisEvent& event); + void handleButtonEvent(const SDL_ControllerButtonEvent& event); +#endif + +#ifdef HAVE_SDL2 + int getInstanceId() const { return _instanceId; } +#endif + +private: +#ifdef HAVE_SDL2 + SDL_GameController* _sdlGameController; + SDL_Joystick* _sdlJoystick; + SDL_JoystickID _instanceId; +#endif +}; + +#endif // hifi_Joystick_h diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/input-plugins/src/input-plugins/StandardController.cpp index 988714a962..5460e22c96 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/input-plugins/src/input-plugins/StandardController.cpp @@ -1,121 +1,121 @@ -// -// StandardController.cpp -// input-plugins/src/input-plugins -// -// Created by Brad Hefta-Gaub on 2015-10-11. -// 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 -// - -#include - -#include - -#include "StandardController.h" - -const float CONTROLLER_THRESHOLD = 0.3f; - -StandardController::~StandardController() { -} - -void StandardController::update(float deltaTime, bool jointsCaptured) { -} - -void StandardController::focusOutEvent() { - _axisStateMap.clear(); - _buttonPressedMap.clear(); -}; - -void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getStandardDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - // Buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "B")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "X")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Y")); - - // DPad - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "DU")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "DD")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "DL")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "DR")); - - // Bumpers - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "RB")); - - // Stick press - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "RS")); - - // Center buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::START), "Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Back")); - - // Analog sticks - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RX), "RX")); - - // Triggers - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "RT")); - - // Poses - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LeftPose), "LeftPose")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RightPose), "RightPose")); - - // Aliases, PlayStation style names - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LB), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RB), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LT), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RT), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::LS), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::RS), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::BACK), "Select")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::A), "Cross")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::B), "Circle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::X), "Square")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::Y), "Triangle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DU), "Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DD), "Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DL), "Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Controllers::DR), "Right")); - - - return availableInputs; - }; - - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - - mapper.registerStandardDevice(proxy); -} - -void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { -} - -UserInputMapper::Input StandardController::makeInput(Controllers::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input StandardController::makeInput(Controllers::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} - -UserInputMapper::Input StandardController::makeInput(Controllers::StandardPoseChannel pose) { - return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); -} +// +// StandardController.cpp +// input-plugins/src/input-plugins +// +// Created by Brad Hefta-Gaub on 2015-10-11. +// 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 +// + +#include + +#include + +#include "StandardController.h" + +const float CONTROLLER_THRESHOLD = 0.3f; + +StandardController::~StandardController() { +} + +void StandardController::update(float deltaTime, bool jointsCaptured) { +} + +void StandardController::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + +void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getStandardDeviceID(); + + auto proxy = std::make_shared(_name); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); + + // Poses + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT), "LeftPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT), "RightPose")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); + + + return availableInputs; + }; + + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + + mapper.registerStandardDevice(proxy); +} + +void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { +} + +UserInputMapper::Input StandardController::makeInput(controller::StandardButtonChannel button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input StandardController::makeInput(controller::StandardAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + +UserInputMapper::Input StandardController::makeInput(controller::StandardPoseChannel pose) { + return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); +} diff --git a/libraries/input-plugins/src/input-plugins/StandardController.h b/libraries/input-plugins/src/input-plugins/StandardController.h index fa660e15b8..ad1329d5ed 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.h +++ b/libraries/input-plugins/src/input-plugins/StandardController.h @@ -1,48 +1,48 @@ -// -// StandardController.h -// input-plugins/src/input-plugins -// -// Created by Brad Hefta-Gaub on 2015-10-11. -// 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 -// - -#ifndef hifi_StandardController_h -#define hifi_StandardController_h - -#include -#include - -#include "InputDevice.h" - -#include "StandardControls.h" - -typedef std::shared_ptr StandardControllerPointer; - -class StandardController : public QObject, public InputDevice { - Q_OBJECT - Q_PROPERTY(QString name READ getName) - -public: - - const QString& getName() const { return _name; } - - // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - - StandardController() : InputDevice("Standard") {} - ~StandardController(); - - UserInputMapper::Input makeInput(Controllers::StandardButtonChannel button); - UserInputMapper::Input makeInput(Controllers::StandardAxisChannel axis); - UserInputMapper::Input makeInput(Controllers::StandardPoseChannel pose); - -private: -}; - -#endif // hifi_StandardController_h +// +// StandardController.h +// input-plugins/src/input-plugins +// +// Created by Brad Hefta-Gaub on 2015-10-11. +// 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 +// + +#ifndef hifi_StandardController_h +#define hifi_StandardController_h + +#include +#include + +#include "InputDevice.h" + +#include "StandardControls.h" + +typedef std::shared_ptr StandardControllerPointer; + +class StandardController : public QObject, public InputDevice { + Q_OBJECT + Q_PROPERTY(QString name READ getName) + +public: + + const QString& getName() const { return _name; } + + // Device functions + virtual void registerToUserInputMapper(UserInputMapper& mapper) override; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + StandardController() : InputDevice("Standard") {} + ~StandardController(); + + UserInputMapper::Input makeInput(controller::StandardButtonChannel button); + UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); + UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); + +private: +}; + +#endif // hifi_StandardController_h diff --git a/libraries/input-plugins/src/input-plugins/StandardControls.h b/libraries/input-plugins/src/input-plugins/StandardControls.h index 9b79bbdae1..e5943ff780 100644 --- a/libraries/input-plugins/src/input-plugins/StandardControls.h +++ b/libraries/input-plugins/src/input-plugins/StandardControls.h @@ -1,55 +1,59 @@ -// -// Created by Bradley Austin Davis 2015/10/09 -// 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 - -namespace Controllers { - - // Needs to match order and values of SDL_GameControllerButton - enum StandardButtonChannel { - // Button quad - A = 0, - B, - X, - Y, - // Center buttons - BACK, - GUIDE, - START, - // Stick press - LS, - RS, - // Bumper press - LB, - RB, - // DPad - DU, - DD, - DL, - DR - }; - - // Needs to match order and values of SDL_GameControllerAxis - enum StandardAxisChannel { - // Left Analog stick - LX = 0, - LY, - // Right Analog stick - RX, - RY, - // Triggers - LT, - RT - }; - - // No correlation to SDL - enum StandardPoseChannel { - LeftPose = 0, - RightPose - }; - -} +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 + +namespace controller { + + // Needs to match order and values of SDL_GameControllerButton + enum StandardButtonChannel { + // Button quad + A = 0, + B, + X, + Y, + // Center buttons + BACK, + GUIDE, + START, + // Stick press + LS, + RS, + // Bumper press + LB, + RB, + // DPad + DU, + DD, + DL, + DR, + NUM_STANDARD_BUTTONS + }; + + // Needs to match order and values of SDL_GameControllerAxis + enum StandardAxisChannel { + // Left Analog stick + LX = 0, + LY, + // Right Analog stick + RX, + RY, + // Triggers + LT, + RT, + NUM_STANDARD_AXES + }; + + // No correlation to SDL + enum StandardPoseChannel { + LEFT = 0, + RIGHT, + HEAD, + NUM_STANDARD_POSES + }; + +} From 0063f9ae1d9e10fce4ef4e25fe5e9cf477c42340 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 15:16:54 -0700 Subject: [PATCH 027/232] Merging old and new controller interfaces --- interface/CMakeLists.txt | 5 +- interface/src/Application.cpp | 6 +- interface/src/Application.h | 2 +- interface/src/devices/3DConnexionClient.h | 2 +- .../ControllerScriptingInterface.cpp | 140 ++-- .../scripting/ControllerScriptingInterface.h | 135 ++-- interface/src/ui/ApplicationCompositor.cpp | 2 +- libraries/controllers/CMakeLists.txt | 2 +- .../controllers/src/controllers/Endpoint.h | 2 +- .../src/controllers}/InputDevice.cpp | 0 .../src/controllers}/InputDevice.h | 0 .../NewControllerScriptingInterface.h | 89 --- ...ngInterface.cpp => ScriptingInterface.cpp} | 123 ++- .../src/controllers/ScriptingInterface.h | 138 ++++ .../src/controllers}/StandardController.cpp | 242 +++--- .../src/controllers}/StandardController.h | 96 +-- .../src/controllers}/StandardControls.h | 118 +-- .../src/controllers}/UserInputMapper.cpp | 736 +++++++++--------- .../src/controllers}/UserInputMapper.h | 56 +- .../controllers/impl/MappingBuilderProxy.cpp | 2 +- .../controllers/impl/MappingBuilderProxy.h | 8 +- .../controllers/impl/RouteBuilderProxy.cpp | 2 +- .../src/controllers/impl/RouteBuilderProxy.h | 8 +- libraries/entities-renderer/CMakeLists.txt | 2 +- libraries/input-plugins/CMakeLists.txt | 2 +- .../src/input-plugins/InputPlugin.cpp | 2 +- .../src/input-plugins/Joystick.cpp | 7 +- .../src/input-plugins/Joystick.h | 4 +- .../src/input-plugins/KeyboardMouseDevice.h | 2 +- .../src/input-plugins/SDL2Manager.h | 4 +- .../src/input-plugins/SixenseManager.h | 3 +- .../src/input-plugins/ViveControllerManager.h | 2 +- libraries/script-engine/CMakeLists.txt | 2 +- .../AbstractControllerScriptingInterface.h | 125 --- .../src/AbstractScriptingServicesInterface.h | 7 +- libraries/script-engine/src/ScriptEngine.cpp | 21 +- libraries/script-engine/src/ScriptEngine.h | 7 +- tests/controllers/qml/content.qml | 43 +- .../controllers/qml/controls/AnalogButton.qml | 2 +- .../controllers/qml/controls/AnalogStick.qml | 4 +- .../qml/controls/ScrollingGraph.qml | 2 +- .../controllers/qml/controls/ToggleButton.qml | 2 +- tests/controllers/src/main.cpp | 30 +- 43 files changed, 1086 insertions(+), 1101 deletions(-) rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/InputDevice.cpp (100%) rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/InputDevice.h (100%) delete mode 100644 libraries/controllers/src/controllers/NewControllerScriptingInterface.h rename libraries/controllers/src/controllers/{NewControllerScriptingInterface.cpp => ScriptingInterface.cpp} (77%) create mode 100644 libraries/controllers/src/controllers/ScriptingInterface.h rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/StandardController.cpp (98%) rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/StandardController.h (96%) rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/StandardControls.h (95%) rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/UserInputMapper.cpp (93%) mode change 100755 => 100644 rename libraries/{input-plugins/src/input-plugins => controllers/src/controllers}/UserInputMapper.h (96%) mode change 100755 => 100644 delete mode 100644 libraries/script-engine/src/AbstractControllerScriptingInterface.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 95de0649ad..43db834c3c 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -98,9 +98,8 @@ endif() # link required hifi libraries link_hifi_libraries(shared octree environment gpu procedural model render fbx networking model-networking entities avatars audio audio-client animation script-engine physics - render-utils entities-renderer ui auto-updater - plugins display-plugins input-plugins - controllers) + render-utils entities-renderer ui auto-updater + controllers plugins display-plugins input-plugins ) target_bullet() target_glew() diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0e5003830..77f353a3ff 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -60,7 +60,7 @@ #include #include #include // this should probably be removed -#include +#include #include #include #include @@ -614,7 +614,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Setup the userInputMapper with the actions auto userInputMapper = DependencyManager::get(); - connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent); + connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &ControllerScriptingInterface::actionEvent); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { if (state) { switch (action) { @@ -2708,7 +2708,7 @@ void Application::update(float deltaTime) { } // Dispatch input events - _controllerScriptingInterface.updateInputControllers(); + _controllerScriptingInterface.update(); // Transfer the user inputs to the driveKeys myAvatar->clearDriveKeys(); diff --git a/interface/src/Application.h b/interface/src/Application.h index dc714ad82a..61421c34d6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -161,7 +161,7 @@ public: ToolWindow* getToolWindow() { return _toolWindow ; } - virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; } + virtual controller::ScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; } virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); QImage renderAvatarBillboard(RenderArgs* renderArgs); diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index cdf8e1e2a1..bddb86a857 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include "InterfaceLogging.h" diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 54aa72da6b..1a212fbea9 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -9,21 +9,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ControllerScriptingInterface.h" + #include #include #include #include -#include - #include "Application.h" #include "devices/MotionTracker.h" -#include "ControllerScriptingInterface.h" // TODO: this needs to be removed, as well as any related controller-specific information #include - ControllerScriptingInterface::ControllerScriptingInterface() : _mouseCaptured(false), _touchCaptured(false), @@ -34,7 +32,6 @@ ControllerScriptingInterface::ControllerScriptingInterface() : } ControllerScriptingInterface::~ControllerScriptingInterface() { - delete _newControllerScriptingInterface; } @@ -126,13 +123,6 @@ void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue); qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); - - wireUpControllers(engine); - - // hack in the new controller scripting interface... - _newControllerScriptingInterface = new controller::NewControllerScriptingInterface(); - engine->registerGlobalObject("NewControllers", _newControllerScriptingInterface); - } void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { @@ -192,6 +182,7 @@ const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const return NULL; } +/* bool ControllerScriptingInterface::isPrimaryButtonPressed() const { const PalmData* primaryPalm = getPrimaryPalm(); if (primaryPalm) { @@ -345,6 +336,7 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex } return glm::vec3(0); // bad index } +*/ bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { return isKeyCaptured(KeyEvent(*event)); @@ -395,96 +387,49 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { return qApp->getUiSize(); } -QString ControllerScriptingInterface::sanatizeName(const QString& name) { - QString cleanName { name }; - cleanName.remove(QRegularExpression{"[\\(\\)\\.\\s]"}); - return cleanName; -} - -void ControllerScriptingInterface::wireUpControllers(ScriptEngine* engine) { - - // Controller.Standard.* - auto standardDevice = DependencyManager::get()->getStandardDevice(); - if (standardDevice) { - auto deviceName = sanatizeName(standardDevice->getName()); - auto deviceInputs = standardDevice->getAvailabeInputs(); - for (const auto& inputMapping : deviceInputs) { - auto input = inputMapping.first; - auto inputName = sanatizeName(inputMapping.second); - QString deviceInputName{ "Controller." + deviceName + "." + inputName }; - engine->registerValue(deviceInputName, input.getID()); - } - } - - // Controller.Hardware.* - auto devices = DependencyManager::get()->getDevices(); - for(const auto& deviceMapping : devices) { - auto device = deviceMapping.second.get(); - auto deviceName = sanatizeName(device->getName()); - auto deviceInputs = device->getAvailabeInputs(); - for (const auto& inputMapping : deviceInputs) { - auto input = inputMapping.first; - auto inputName = sanatizeName(inputMapping.second); - QString deviceInputName { "Controller.Hardware." + deviceName + "." + inputName }; - engine->registerValue(deviceInputName, input.getID()); - } - } - - // Controller.Actions.* - auto actionNames = DependencyManager::get()->getActionNames(); - int actionNumber = 0; - for (const auto& actionName : actionNames) { - QString safeActionName { "Controller.Actions." + sanatizeName(actionName) }; - engine->registerValue(safeActionName, actionNumber); - actionNumber++; - } -} - -AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { +controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { // This is where we retreive the Device Tracker category and then the sub tracker within it - //TODO C++11 auto icIt = _inputControllers.find(0); - InputControllerMap::iterator icIt = _inputControllers.find(0); - + auto icIt = _inputControllers.find(0); if (icIt != _inputControllers.end()) { return (*icIt).second; - } else { + } - // Look for device - DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); - if (deviceID < 0) { - deviceID = 0; - } - // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) - // in the near future we need to change that to a real mapping between the devices and the deviceName - // ALso we need to expand the spec so we can fall back on the "default" controller per categories - if (deviceID >= 0) { - // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices - MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); - if (motionTracker) { - MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); - if (trackerID >= 0) { - AbstractInputController* inputController = new InputController(deviceID, trackerID, this); + // Look for device + DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); + if (deviceID < 0) { + deviceID = 0; + } + // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) + // in the near future we need to change that to a real mapping between the devices and the deviceName + // ALso we need to expand the spec so we can fall back on the "default" controller per categories - _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController)); - - return inputController; - } + if (deviceID >= 0) { + // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices + MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); + if (motionTracker) { + MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); + if (trackerID >= 0) { + controller::InputController::Pointer inputController = std::make_shared(deviceID, trackerID, this); + controller::InputController::Key key = inputController->getKey(); + _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController)); + return inputController; } } - - return 0; } + + return controller::InputController::Pointer(); } -void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) { +void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) { _inputControllers.erase(input->getKey()); } -void ControllerScriptingInterface::updateInputControllers() { - //TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) { - for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) { - (*it).second->update(); +void ControllerScriptingInterface::update() { + controller::ScriptingInterface::update(); + + for (auto entry : _inputControllers) { + entry.second->update(); } } @@ -545,7 +490,6 @@ QVector ControllerScriptingInterface::getActionNames() const { } InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : - AbstractInputController(), _deviceTrackerId(deviceTrackerId), _subTrackerId(subTrackerId), _isActive(false) @@ -568,7 +512,7 @@ void InputController::update() { joint->getLocFrame().getRotation(_eventCache.locRotation); _isActive = true; - emit spatialEvent(_eventCache); + //emit spatialEvent(_eventCache); } } } @@ -580,3 +524,19 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; InputController::Key InputController::getKey() const { return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId); } + + +void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } +void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } + +void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } +void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); } +void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); } +void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } + +void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } +void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); } +void ControllerScriptingInterface::emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); } + +void ControllerScriptingInterface::emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); } + diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index dfe87043cd..652bd640b1 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -14,15 +14,20 @@ #include -#include +#include +#include + +#include +#include +#include +#include +#include +#include +class ScriptEngine; -#include class PalmData; -namespace controller { - class NewControllerScriptingInterface; -} -class InputController : public AbstractInputController { +class InputController : public controller::InputController { Q_OBJECT public: @@ -53,30 +58,53 @@ signals: /// handles scripting of input controller commands from JS -class ControllerScriptingInterface : public AbstractControllerScriptingInterface { +class ControllerScriptingInterface : public controller::ScriptingInterface { Q_OBJECT + public: ControllerScriptingInterface(); ~ControllerScriptingInterface(); + Q_INVOKABLE QVector getAllActions(); + + Q_INVOKABLE bool addInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE bool removeInputChannel(UserInputMapper::InputChannel inputChannel); + Q_INVOKABLE QVector getInputChannelsForAction(UserInputMapper::Action action); + + Q_INVOKABLE QVector getAvailableInputs(unsigned int device); + Q_INVOKABLE QVector getAllInputsForDevice(unsigned int device); + + Q_INVOKABLE QString getDeviceName(unsigned int device); + + Q_INVOKABLE float getActionValue(int action); + + Q_INVOKABLE void resetDevice(unsigned int device); + Q_INVOKABLE void resetAllDeviceBindings(); + Q_INVOKABLE int findDevice(QString name); + Q_INVOKABLE QVector getDeviceNames(); + + Q_INVOKABLE int findAction(QString actionName); + Q_INVOKABLE QVector getActionNames() const; + + virtual void registerControllerTypes(ScriptEngine* engine); - void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } - void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } + void emitKeyPressEvent(QKeyEvent* event); + void emitKeyReleaseEvent(QKeyEvent* event); void handleMetaEvent(HFMetaEvent* event); - void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } - void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); } - void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); } - void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } + void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); + void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); + void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0); + void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); - void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } - void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); } - void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); } + void emitTouchBeginEvent(const TouchEvent& event); + void emitTouchEndEvent(const TouchEvent& event); + void emitTouchUpdateEvent(const TouchEvent& event); - void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); } + void emitWheelEvent(QWheelEvent* event); bool isKeyCaptured(QKeyEvent* event) const; bool isKeyCaptured(const KeyEvent& event) const; @@ -86,48 +114,10 @@ public: bool areActionsCaptured() const { return _actionsCaptured; } bool isJoystickCaptured(int joystickIndex) const; - void updateInputControllers(); + virtual void update() override; public slots: - Q_INVOKABLE virtual QVector getAllActions(); - - Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel); - Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel); - Q_INVOKABLE virtual QVector getInputChannelsForAction(UserInputMapper::Action action); - - Q_INVOKABLE virtual QVector getAvailableInputs(unsigned int device); - Q_INVOKABLE virtual QVector getAllInputsForDevice(unsigned int device); - - Q_INVOKABLE virtual QString getDeviceName(unsigned int device); - - Q_INVOKABLE virtual float getActionValue(int action); - Q_INVOKABLE virtual void resetDevice(unsigned int device); - Q_INVOKABLE virtual void resetAllDeviceBindings(); - Q_INVOKABLE virtual int findDevice(QString name); - Q_INVOKABLE virtual QVector getDeviceNames(); - - Q_INVOKABLE virtual int findAction(QString actionName); - Q_INVOKABLE virtual QVector getActionNames() const; - - virtual bool isPrimaryButtonPressed() const; - virtual glm::vec2 getPrimaryJoystickPosition() const; - - virtual int getNumberOfButtons() const; - virtual bool isButtonPressed(int buttonIndex) const; - - virtual int getNumberOfTriggers() const; - virtual float getTriggerValue(int triggerIndex) const; - - virtual int getNumberOfJoysticks() const; - virtual glm::vec2 getJoystickPosition(int joystickIndex) const; - - virtual int getNumberOfSpatialControls() const; - virtual glm::vec3 getSpatialControlPosition(int controlIndex) const; - virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const; - virtual glm::vec3 getSpatialControlNormal(int controlIndex) const; - virtual glm::quat getSpatialControlRawRotation(int controlIndex) const; - virtual glm::vec3 getSpatialControlRawAngularVelocity(int controlIndex) const; virtual void captureKeyEvents(const KeyEvent& event); virtual void releaseKeyEvents(const KeyEvent& event); @@ -149,9 +139,31 @@ public slots: virtual glm::vec2 getViewportDimensions() const; /// Factory to create an InputController - virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker); + virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker); + virtual void releaseInputController(controller::InputController::Pointer input); - virtual void releaseInputController(AbstractInputController* input); +signals: + void keyPressEvent(const KeyEvent& event); + void keyReleaseEvent(const KeyEvent& event); + + void actionStartEvent(const HFActionEvent& event); + void actionEndEvent(const HFActionEvent& event); + + void backStartEvent(); + void backEndEvent(); + + void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0); + void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0); + void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0); + void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0); + + void touchBeginEvent(const TouchEvent& event); + void touchEndEvent(const TouchEvent& event); + void touchUpdateEvent(const TouchEvent& event); + + 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 @@ -168,12 +180,9 @@ private: QMultiMap _capturedKeys; QSet _capturedJoysticks; - typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap; + using InputKey = controller::InputController::Key; + using InputControllerMap = std::map; InputControllerMap _inputControllers; - - void wireUpControllers(ScriptEngine* engine); - - controller::NewControllerScriptingInterface* _newControllerScriptingInterface = nullptr; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 4e5dd0da0c..4fc2f3ddb4 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -27,7 +27,7 @@ #include "Application.h" #include // TODO: any references to sixense should be removed here -#include +#include // Used to animate the magnification windows diff --git a/libraries/controllers/CMakeLists.txt b/libraries/controllers/CMakeLists.txt index fbabbe1463..5beffce461 100644 --- a/libraries/controllers/CMakeLists.txt +++ b/libraries/controllers/CMakeLists.txt @@ -4,7 +4,7 @@ set(TARGET_NAME controllers) setup_hifi_library(Script) # use setup_hifi_library macro to setup our project and link appropriate Qt modules -link_hifi_libraries(shared plugins input-plugins) +link_hifi_libraries(shared) GroupSources("src/controllers") diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index bea33517f5..5c6e6c028b 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -14,7 +14,7 @@ #include #include -#include +#include "UserInputMapper.h" class QScriptValue; diff --git a/libraries/input-plugins/src/input-plugins/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp similarity index 100% rename from libraries/input-plugins/src/input-plugins/InputDevice.cpp rename to libraries/controllers/src/controllers/InputDevice.cpp diff --git a/libraries/input-plugins/src/input-plugins/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h similarity index 100% rename from libraries/input-plugins/src/input-plugins/InputDevice.h rename to libraries/controllers/src/controllers/InputDevice.h diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h deleted file mode 100644 index 659c8bfd05..0000000000 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// Created by Bradley Austin Davis 2015/10/09 -// 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_NewControllerScriptingInterface_h -#define hifi_Controllers_NewControllerScriptingInterface_h - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include - -#include "Mapping.h" - -class QScriptValue; - -namespace controller { - class NewControllerScriptingInterface : public QObject { - Q_OBJECT - Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL) - Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL) - Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL) - - public: - NewControllerScriptingInterface(); - Q_INVOKABLE float getValue(const int& source); - - Q_INVOKABLE void update(); - Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); - Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); - Q_INVOKABLE void disableMapping(const QString& mappingName) { - enableMapping(mappingName, false); - } - - - const QVariantMap& getHardware() { return _hardware; } - const QVariantMap& getActions() { return _actions; } - const QVariantMap& getStandard() { return _standard; } - - private: - - // FIXME move to unordered set / map - using MappingMap = std::map; - using MappingStack = std::list; - using InputToEndpointMap = std::map; - using EndpointSet = std::unordered_set; - using ValueMap = std::map; - using EndpointPair = std::pair; - using EndpointPairMap = std::map; - - void update(Mapping::Pointer& mapping, EndpointSet& consumed); - float getValue(const Endpoint::Pointer& endpoint); - Endpoint::Pointer endpointFor(const QJSValue& endpoint); - Endpoint::Pointer endpointFor(const QScriptValue& endpoint); - Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); - Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); - - friend class MappingBuilderProxy; - friend class RouteBuilderProxy; - private: - uint16_t _nextFunctionId; - InputToEndpointMap _endpoints; - EndpointPairMap _compositeEndpoints; - - ValueMap _overrideValues; - MappingMap _mappingsByName; - MappingStack _activeMappings; - - QVariantMap _hardware; - QVariantMap _actions; - QVariantMap _standard; - }; -} - - -#endif diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp similarity index 77% rename from libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp rename to libraries/controllers/src/controllers/ScriptingInterface.cpp index 4bc5b9eeb5..9d2cdfd2de 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "NewControllerScriptingInterface.h" +#include "ScriptingInterface.h" #include #include @@ -14,19 +14,15 @@ #include #include -#include -#include -#include -#include #include "impl/MappingBuilderProxy.h" #include "Logging.h" +#include "InputDevice.h" static const uint16_t ACTIONS_DEVICE = UserInputMapper::Input::INVALID_DEVICE - (uint16_t)1; namespace controller { - class VirtualEndpoint : public Endpoint { public: VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1)) @@ -97,18 +93,15 @@ namespace controller { Endpoint::Pointer _second; }; - QString sanatizeName(const QString& name) { - QString cleanName{ name }; - cleanName.remove(QRegularExpression{ "[\\(\\)\\.\\s]" }); - return cleanName; - } + + QRegularExpression ScriptingInterface::SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; QVariantMap createDeviceMap(const UserInputMapper::DeviceProxy* device) { auto userInputMapper = DependencyManager::get(); QVariantMap deviceMap; for (const auto& inputMapping : device->getAvailabeInputs()) { const auto& input = inputMapping.first; - const auto inputName = sanatizeName(inputMapping.second); + const auto inputName = QString(inputMapping.second).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() << QString::number(input.getID(), 16) << ": " << inputName; deviceMap.insert(inputName, input.getID()); @@ -116,12 +109,12 @@ namespace controller { return deviceMap; } - NewControllerScriptingInterface::NewControllerScriptingInterface() { + ScriptingInterface::ScriptingInterface() { auto userInputMapper = DependencyManager::get(); auto devices = userInputMapper->getDevices(); for (const auto& deviceMapping : devices) { auto device = deviceMapping.second.get(); - auto deviceName = sanatizeName(device->getName()); + auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; // Expose the IDs to JS _hardware.insert(deviceName, createDeviceMap(device)); @@ -164,7 +157,8 @@ namespace controller { UserInputMapper::Input actionInput(ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); // Expose the IDs to JS - _actions.insert(sanatizeName(actionName), actionInput.getID()); + QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); + _actions.insert(cleanActionName, actionInput.getID()); // Create the endpoints // FIXME action endpoints need to accumulate values, and have them cleared at each frame @@ -172,7 +166,7 @@ namespace controller { } } - QObject* NewControllerScriptingInterface::newMapping(const QString& mappingName) { + QObject* ScriptingInterface::newMapping(const QString& mappingName) { if (_mappingsByName.count(mappingName)) { qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; } @@ -182,7 +176,7 @@ namespace controller { return new MappingBuilderProxy(*this, mapping); } - void NewControllerScriptingInterface::enableMapping(const QString& mappingName, bool enable) { + void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) { auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; @@ -202,7 +196,7 @@ namespace controller { } } - float NewControllerScriptingInterface::getValue(const int& source) { + float ScriptingInterface::getValue(const int& source) const { // return (sin(secTimestampNow()) + 1.0f) / 2.0f; UserInputMapper::Input input(source); auto iterator = _endpoints.find(input); @@ -214,7 +208,7 @@ namespace controller { return getValue(endpoint); } - float NewControllerScriptingInterface::getValue(const Endpoint::Pointer& endpoint) { + float ScriptingInterface::getValue(const Endpoint::Pointer& endpoint) const { auto valuesIterator = _overrideValues.find(endpoint); if (_overrideValues.end() != valuesIterator) { return valuesIterator->second; @@ -223,19 +217,20 @@ namespace controller { return endpoint->value(); } - - void NewControllerScriptingInterface::update() { - static float last = secTimestampNow(); - float now = secTimestampNow(); - float delta = now - last; - last = now; + float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const { + return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::BUTTON).getID()); + } - foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - inputPlugin->pluginUpdate(delta, false); - } + float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const { + return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::AXIS).getID()); + } + glm::mat4 ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { + return glm::mat4(); + } + + void ScriptingInterface::update() { auto userInputMapper = DependencyManager::get(); - userInputMapper->update(delta); _overrideValues.clear(); EndpointSet readEndpoints; @@ -295,9 +290,7 @@ namespace controller { } } - - - Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const QJSValue& endpoint) { + Endpoint::Pointer ScriptingInterface::endpointFor(const QJSValue& endpoint) { if (endpoint.isNumber()) { return endpointFor(UserInputMapper::Input(endpoint.toInt())); } @@ -311,7 +304,7 @@ namespace controller { return Endpoint::Pointer(); } - Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const QScriptValue& endpoint) { + Endpoint::Pointer ScriptingInterface::endpointFor(const QScriptValue& endpoint) { if (endpoint.isNumber()) { return endpointFor(UserInputMapper::Input(endpoint.toInt32())); } @@ -325,7 +318,7 @@ namespace controller { return Endpoint::Pointer(); } - Endpoint::Pointer NewControllerScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { + Endpoint::Pointer ScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { auto iterator = _endpoints.find(inputId); if (_endpoints.end() == iterator) { qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); @@ -334,7 +327,7 @@ namespace controller { return iterator->second; } - Endpoint::Pointer NewControllerScriptingInterface::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { + Endpoint::Pointer ScriptingInterface::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { EndpointPair pair(first, second); Endpoint::Pointer result; auto iterator = _compositeEndpoints.find(pair); @@ -347,6 +340,66 @@ namespace controller { return result; } + bool ScriptingInterface::isPrimaryButtonPressed() const { + return isButtonPressed(StandardButtonChannel::A); + } + + glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const { + return getJoystickPosition(0); + } + + int ScriptingInterface::getNumberOfButtons() const { + return StandardButtonChannel::NUM_STANDARD_BUTTONS; + } + + bool ScriptingInterface::isButtonPressed(int buttonIndex) const { + return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true; + } + + int ScriptingInterface::getNumberOfTriggers() const { + return 2; + } + + float ScriptingInterface::getTriggerValue(int triggerIndex) const { + return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT); + } + + int ScriptingInterface::getNumberOfJoysticks() const { + return 2; + } + + glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const { + StandardAxisChannel xid = StandardAxisChannel::LX; + StandardAxisChannel yid = StandardAxisChannel::LY; + if (joystickIndex != 0) { + xid = StandardAxisChannel::RX; + yid = StandardAxisChannel::RY; + } + vec2 result; + result.x = getAxisValue(xid); + result.y = getAxisValue(yid); + return result; + } + + int ScriptingInterface::getNumberOfSpatialControls() const { + return 2; + } + + glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const { + return vec3(); + } + + glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const { + return vec3(); + } + + glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const { + return vec3(); + } + + glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { + return quat(); + } } // namespace controllers //var mapping = Controller.newMapping(); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h new file mode 100644 index 0000000000..043e06a67f --- /dev/null +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -0,0 +1,138 @@ +// +// AbstractControllerScriptingInterface.h +// libraries/script-engine/src +// +// Created by Brad Hefta-Gaub on 12/17/13. +// Copyright 2013 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_AbstractControllerScriptingInterface_h +#define hifi_AbstractControllerScriptingInterface_h + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "UserInputMapper.h" +#include "StandardControls.h" +#include "Mapping.h" + +namespace controller { + class InputController : public QObject { + Q_OBJECT + + public: + using Key = unsigned int; + using Pointer = std::shared_ptr; + + virtual void update() = 0; + virtual Key getKey() const = 0; + + public slots: + virtual bool isActive() const = 0; + virtual glm::vec3 getAbsTranslation() const = 0; + virtual glm::quat getAbsRotation() const = 0; + virtual glm::vec3 getLocTranslation() const = 0; + virtual glm::quat getLocRotation() const = 0; + + signals: + //void spatialEvent(const SpatialEvent& event); + }; + + /// handles scripting of input controller commands from JS + class ScriptingInterface : public QObject { + Q_OBJECT + Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL) + Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL) + Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL) + + public: + ScriptingInterface(); + + Q_INVOKABLE float getValue(const int& source) const; + Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const; + Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const; + Q_INVOKABLE glm::mat4 getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; + Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); + Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); + Q_INVOKABLE void disableMapping(const QString& mappingName) { + enableMapping(mappingName, false); + } + + Q_INVOKABLE bool isPrimaryButtonPressed() const; + Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; + + Q_INVOKABLE int getNumberOfButtons() const; + Q_INVOKABLE bool isButtonPressed(int buttonIndex) const; + + Q_INVOKABLE int getNumberOfTriggers() const; + Q_INVOKABLE float getTriggerValue(int triggerIndex) const; + + Q_INVOKABLE int getNumberOfJoysticks() const; + Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const; + + Q_INVOKABLE int getNumberOfSpatialControls() const; + Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const; + Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const; + Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const; + Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const; + + Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; } + Q_INVOKABLE const QVariantMap& getActions() { return _actions; } + Q_INVOKABLE const QVariantMap& getStandard() { return _standard; } + + static QRegularExpression SANITIZE_NAME_EXPRESSION; + + public slots: + virtual void update(); + //virtual void registerControllerTypes(ScriptEngine* engine) = 0; + + private: + friend class MappingBuilderProxy; + friend class RouteBuilderProxy; + + // FIXME move to unordered set / map + using MappingMap = std::map; + using MappingStack = std::list; + using InputToEndpointMap = std::map; + using EndpointSet = std::unordered_set; + using ValueMap = std::map; + using EndpointPair = std::pair; + using EndpointPairMap = std::map; + + void update(Mapping::Pointer& mapping, EndpointSet& consumed); + float getValue(const Endpoint::Pointer& endpoint) const; + Endpoint::Pointer endpointFor(const QJSValue& endpoint); + Endpoint::Pointer endpointFor(const QScriptValue& endpoint); + Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); + Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); + + QVariantMap _hardware; + QVariantMap _actions; + QVariantMap _standard; + + InputToEndpointMap _endpoints; + EndpointPairMap _compositeEndpoints; + + ValueMap _overrideValues; + MappingMap _mappingsByName; + MappingStack _activeMappings; + }; +} + + +#endif // hifi_AbstractControllerScriptingInterface_h diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp similarity index 98% rename from libraries/input-plugins/src/input-plugins/StandardController.cpp rename to libraries/controllers/src/controllers/StandardController.cpp index 5460e22c96..9c423eded2 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -1,121 +1,121 @@ -// -// StandardController.cpp -// input-plugins/src/input-plugins -// -// Created by Brad Hefta-Gaub on 2015-10-11. -// 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 -// - -#include - -#include - -#include "StandardController.h" - -const float CONTROLLER_THRESHOLD = 0.3f; - -StandardController::~StandardController() { -} - -void StandardController::update(float deltaTime, bool jointsCaptured) { -} - -void StandardController::focusOutEvent() { - _axisStateMap.clear(); - _buttonPressedMap.clear(); -}; - -void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getStandardDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - // Buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); - - // DPad - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); - - // Bumpers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); - - // Stick press - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); - - // Center buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); - - // Analog sticks - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); - - // Triggers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); - - // Poses - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT), "LeftPose")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT), "RightPose")); - - // Aliases, PlayStation style names - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); - - - return availableInputs; - }; - - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - - mapper.registerStandardDevice(proxy); -} - -void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { -} - -UserInputMapper::Input StandardController::makeInput(controller::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input StandardController::makeInput(controller::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} - -UserInputMapper::Input StandardController::makeInput(controller::StandardPoseChannel pose) { - return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); -} +// +// StandardController.cpp +// input-plugins/src/input-plugins +// +// Created by Brad Hefta-Gaub on 2015-10-11. +// 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 +// + +#include + +#include + +#include "StandardController.h" + +const float CONTROLLER_THRESHOLD = 0.3f; + +StandardController::~StandardController() { +} + +void StandardController::update(float deltaTime, bool jointsCaptured) { +} + +void StandardController::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + +void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getStandardDeviceID(); + + auto proxy = std::make_shared(_name); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); + + // Poses + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT), "LeftPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT), "RightPose")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); + + + return availableInputs; + }; + + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + + mapper.registerStandardDevice(proxy); +} + +void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { +} + +UserInputMapper::Input StandardController::makeInput(controller::StandardButtonChannel button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input StandardController::makeInput(controller::StandardAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + +UserInputMapper::Input StandardController::makeInput(controller::StandardPoseChannel pose) { + return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); +} diff --git a/libraries/input-plugins/src/input-plugins/StandardController.h b/libraries/controllers/src/controllers/StandardController.h similarity index 96% rename from libraries/input-plugins/src/input-plugins/StandardController.h rename to libraries/controllers/src/controllers/StandardController.h index ad1329d5ed..c393af80f4 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.h +++ b/libraries/controllers/src/controllers/StandardController.h @@ -1,48 +1,48 @@ -// -// StandardController.h -// input-plugins/src/input-plugins -// -// Created by Brad Hefta-Gaub on 2015-10-11. -// 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 -// - -#ifndef hifi_StandardController_h -#define hifi_StandardController_h - -#include -#include - -#include "InputDevice.h" - -#include "StandardControls.h" - -typedef std::shared_ptr StandardControllerPointer; - -class StandardController : public QObject, public InputDevice { - Q_OBJECT - Q_PROPERTY(QString name READ getName) - -public: - - const QString& getName() const { return _name; } - - // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - - StandardController() : InputDevice("Standard") {} - ~StandardController(); - - UserInputMapper::Input makeInput(controller::StandardButtonChannel button); - UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); - UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); - -private: -}; - -#endif // hifi_StandardController_h +// +// StandardController.h +// input-plugins/src/input-plugins +// +// Created by Brad Hefta-Gaub on 2015-10-11. +// 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 +// + +#ifndef hifi_StandardController_h +#define hifi_StandardController_h + +#include +#include + +#include "InputDevice.h" + +#include "StandardControls.h" + +typedef std::shared_ptr StandardControllerPointer; + +class StandardController : public QObject, public InputDevice { + Q_OBJECT + Q_PROPERTY(QString name READ getName) + +public: + + const QString& getName() const { return _name; } + + // Device functions + virtual void registerToUserInputMapper(UserInputMapper& mapper) override; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + StandardController() : InputDevice("Standard") {} + ~StandardController(); + + UserInputMapper::Input makeInput(controller::StandardButtonChannel button); + UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); + UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); + +private: +}; + +#endif // hifi_StandardController_h diff --git a/libraries/input-plugins/src/input-plugins/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h similarity index 95% rename from libraries/input-plugins/src/input-plugins/StandardControls.h rename to libraries/controllers/src/controllers/StandardControls.h index e5943ff780..505b4f85c7 100644 --- a/libraries/input-plugins/src/input-plugins/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -1,59 +1,59 @@ -// -// Created by Bradley Austin Davis 2015/10/09 -// 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 - -namespace controller { - - // Needs to match order and values of SDL_GameControllerButton - enum StandardButtonChannel { - // Button quad - A = 0, - B, - X, - Y, - // Center buttons - BACK, - GUIDE, - START, - // Stick press - LS, - RS, - // Bumper press - LB, - RB, - // DPad - DU, - DD, - DL, - DR, - NUM_STANDARD_BUTTONS - }; - - // Needs to match order and values of SDL_GameControllerAxis - enum StandardAxisChannel { - // Left Analog stick - LX = 0, - LY, - // Right Analog stick - RX, - RY, - // Triggers - LT, - RT, - NUM_STANDARD_AXES - }; - - // No correlation to SDL - enum StandardPoseChannel { - LEFT = 0, - RIGHT, - HEAD, - NUM_STANDARD_POSES - }; - -} +// +// Created by Bradley Austin Davis 2015/10/09 +// 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 + +namespace controller { + + // Needs to match order and values of SDL_GameControllerButton + enum StandardButtonChannel { + // Button quad + A = 0, + B, + X, + Y, + // Center buttons + BACK, + GUIDE, + START, + // Stick press + LS, + RS, + // Bumper press + LB, + RB, + // DPad + DU, + DD, + DL, + DR, + NUM_STANDARD_BUTTONS + }; + + // Needs to match order and values of SDL_GameControllerAxis + enum StandardAxisChannel { + // Left Analog stick + LX = 0, + LY, + // Right Analog stick + RX, + RY, + // Triggers + LT, + RT, + NUM_STANDARD_AXES + }; + + // No correlation to SDL + enum StandardPoseChannel { + LEFT = 0, + RIGHT, + HEAD, + NUM_STANDARD_POSES + }; + +} diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp old mode 100755 new mode 100644 similarity index 93% rename from libraries/input-plugins/src/input-plugins/UserInputMapper.cpp rename to libraries/controllers/src/controllers/UserInputMapper.cpp index c29acc09af..8fc44ebf5b --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -1,368 +1,368 @@ -// -// UserInputMapper.cpp -// input-plugins/src/input-plugins -// -// Created by Sam Gateau on 4/27/15. -// 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 -// - -#include "UserInputMapper.h" -#include "StandardController.h" - -const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX); -const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); -const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); -const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); - -// Default contruct allocate the poutput size with the current hardcoded action channels -UserInputMapper::UserInputMapper() { - registerStandardDevice(); - assignDefaulActionScales(); - createActionNames(); -} - -UserInputMapper::~UserInputMapper() { -} - - -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ - proxy->_name += " (" + QString::number(deviceID) + ")"; - _registeredDevices[deviceID] = proxy; - return true; -} - -UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { - auto device = _registeredDevices.find(input.getDevice()); - if (device != _registeredDevices.end()) { - return (device->second); - } else { - return DeviceProxy::Pointer(); - } -} - -QString UserInputMapper::getDeviceName(uint16 deviceID) { - if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->_name; - } - return QString("unknown"); -} - - -void UserInputMapper::resetAllDeviceBindings() { - for (auto device : _registeredDevices) { - device.second->resetDeviceBindings(); - } -} - -void UserInputMapper::resetDevice(uint16 deviceID) { - auto device = _registeredDevices.find(deviceID); - if (device != _registeredDevices.end()) { - device->second->resetDeviceBindings(); - } -} - -int UserInputMapper::findDevice(QString name) { - for (auto device : _registeredDevices) { - if (device.second->_name.split(" (")[0] == name) { - return device.first; - } - } - return 0; -} - -QVector UserInputMapper::getDeviceNames() { - QVector result; - for (auto device : _registeredDevices) { - QString deviceName = device.second->_name.split(" (")[0]; - result << deviceName; - } - return result; -} - - -bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { - return addInputChannel(action, input, Input(), scale); -} - -bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { - // Check that the device is registered - if (!getDeviceProxy(input)) { - qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; - return false; - } - - auto inputChannel = InputChannel(input, modifier, action, scale); - - // Insert or replace the input to modifiers - if (inputChannel.hasModifier()) { - auto& modifiers = _inputToModifiersMap[input.getID()]; - modifiers.push_back(inputChannel._modifier); - std::sort(modifiers.begin(), modifiers.end()); - } - - // Now update the action To Inputs side of things - _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); - - return true; -} - -int UserInputMapper::addInputChannels(const InputChannels& channels) { - int nbAdded = 0; - for (auto& channel : channels) { - nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); - } - return nbAdded; -} - -bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { - // Remove from Input to Modifiers map - if (inputChannel.hasModifier()) { - _inputToModifiersMap.erase(inputChannel._input.getID()); - } - - // Remove from Action to Inputs map - std::pair ret; - ret = _actionToInputsMap.equal_range(inputChannel._action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - if (it->second == inputChannel) { - _actionToInputsMap.erase(it); - return true; - } - } - - return false; -} - -void UserInputMapper::removeAllInputChannels() { - _inputToModifiersMap.clear(); - _actionToInputsMap.clear(); -} - -void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { - QVector channels = getAllInputsForDevice(device); - for (auto& channel : channels) { - removeInputChannel(channel); - } -} - -void UserInputMapper::removeDevice(int device) { - removeAllInputChannelsForDevice((uint16) device); - _registeredDevices.erase(device); -} - -int UserInputMapper::getInputChannels(InputChannels& channels) const { - for (auto& channel : _actionToInputsMap) { - channels.push_back(channel.second); - } - - return _actionToInputsMap.size(); -} - -QVector UserInputMapper::getAllInputsForDevice(uint16 device) { - InputChannels allChannels; - getInputChannels(allChannels); - - QVector channels; - for (InputChannel inputChannel : allChannels) { - if (inputChannel._input._device == device) { - channels.push_back(inputChannel); - } - } - - return channels; -} - -void UserInputMapper::update(float deltaTime) { - - // Reset the axis state for next loop - for (auto& channel : _actionStates) { - channel = 0.0f; - } - - for (auto& channel : _poseStates) { - channel = PoseValue(); - } - - int currentTimestamp = 0; - - for (auto& channelInput : _actionToInputsMap) { - auto& inputMapping = channelInput.second; - auto& inputID = inputMapping._input; - bool enabled = true; - - // Check if this input channel has modifiers and collect the possibilities - auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); - if (modifiersIt != _inputToModifiersMap.end()) { - Modifiers validModifiers; - bool isActiveModifier = false; - for (auto& modifier : modifiersIt->second) { - auto deviceProxy = getDeviceProxy(modifier); - if (deviceProxy->getButton(modifier, currentTimestamp)) { - validModifiers.push_back(modifier); - isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); - } - } - enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; - } - - // if enabled: default input or all modifiers on - if (enabled) { - auto deviceProxy = getDeviceProxy(inputID); - switch (inputMapping._input.getType()) { - case ChannelType::BUTTON: { - _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime - break; - } - case ChannelType::AXIS: { - _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); - break; - } - case ChannelType::POSE: { - if (!_poseStates[channelInput.first].isValid()) { - _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); - } - break; - } - default: { - break; //silence please - } - } - } else{ - // Channel input not enabled - enabled = false; - } - } - - // Scale all the channel step with the scale - static const float EPSILON = 0.01f; - for (auto i = 0; i < NUM_ACTIONS; i++) { - _actionStates[i] *= _actionScales[i]; - // Emit only on change, and emit when moving back to 0 - if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { - _lastActionStates[i] = _actionStates[i]; - emit actionEvent(i, _actionStates[i]); - } - // TODO: emit signal for pose changes - } -} - -QVector UserInputMapper::getAllActions() const { - QVector actions; - for (auto i = 0; i < NUM_ACTIONS; i++) { - actions.append(Action(i)); - } - return actions; -} - -QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { - QVector inputChannels; - std::pair ret; - ret = _actionToInputsMap.equal_range(action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - inputChannels.append(it->second); - } - return inputChannels; -} - -int UserInputMapper::findAction(const QString& actionName) const { - auto actions = getAllActions(); - for (auto action : actions) { - if (getActionName(action) == actionName) { - return action; - } - } - // If the action isn't found, return -1 - return -1; -} - -QVector UserInputMapper::getActionNames() const { - QVector result; - for (auto i = 0; i < NUM_ACTIONS; i++) { - result << _actionNames[i]; - } - return result; -} - -void UserInputMapper::assignDefaulActionScales() { - _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit - _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit - _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit - _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit - _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit - _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit - _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit - _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit - _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit - _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit - _actionScales[BOOM_IN] = 0.5f; // .5m per unit - _actionScales[BOOM_OUT] = 0.5f; // .5m per unit - _actionScales[LEFT_HAND] = 1.0f; // default - _actionScales[RIGHT_HAND] = 1.0f; // default - _actionScales[LEFT_HAND_CLICK] = 1.0f; // on - _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on - _actionStates[SHIFT] = 1.0f; // on - _actionStates[ACTION1] = 1.0f; // default - _actionStates[ACTION2] = 1.0f; // default - _actionStates[TranslateX] = 1.0f; // default - _actionStates[TranslateY] = 1.0f; // default - _actionStates[TranslateZ] = 1.0f; // default - _actionStates[Roll] = 1.0f; // default - _actionStates[Pitch] = 1.0f; // default - _actionStates[Yaw] = 1.0f; // default -} - -// This is only necessary as long as the actions are hardcoded -// Eventually you can just add the string when you add the action -void UserInputMapper::createActionNames() { - _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; - _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; - _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; - _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; - _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; - _actionNames[VERTICAL_UP] = "VERTICAL_UP"; - _actionNames[YAW_LEFT] = "YAW_LEFT"; - _actionNames[YAW_RIGHT] = "YAW_RIGHT"; - _actionNames[PITCH_DOWN] = "PITCH_DOWN"; - _actionNames[PITCH_UP] = "PITCH_UP"; - _actionNames[BOOM_IN] = "BOOM_IN"; - _actionNames[BOOM_OUT] = "BOOM_OUT"; - _actionNames[LEFT_HAND] = "LEFT_HAND"; - _actionNames[RIGHT_HAND] = "RIGHT_HAND"; - _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; - _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; - _actionNames[SHIFT] = "SHIFT"; - _actionNames[ACTION1] = "ACTION1"; - _actionNames[ACTION2] = "ACTION2"; - _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; - _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; - _actionNames[TranslateX] = "TranslateX"; - _actionNames[TranslateY] = "TranslateY"; - _actionNames[TranslateZ] = "TranslateZ"; - _actionNames[Roll] = "Roll"; - _actionNames[Pitch] = "Pitch"; - _actionNames[Yaw] = "Yaw"; -} - -void UserInputMapper::registerStandardDevice() { - _standardController = std::make_shared(); - _standardController->registerToUserInputMapper(*this); -} - -float UserInputMapper::DeviceProxy::getValue(const Input& input, int timestamp) const { - switch (input.getType()) { - case UserInputMapper::ChannelType::BUTTON: - return getButton(input, timestamp) ? 1.0f : 0.0f; - - case UserInputMapper::ChannelType::AXIS: - return getAxis(input, timestamp); - - case UserInputMapper::ChannelType::POSE: - return getPose(input, timestamp)._valid ? 1.0f : 0.0f; - - default: - return 0.0f; - } -} +// +// UserInputMapper.cpp +// input-plugins/src/input-plugins +// +// Created by Sam Gateau on 4/27/15. +// 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 +// + +#include "UserInputMapper.h" +#include "StandardController.h" + +const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX); +const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); +const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); +const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); + +// Default contruct allocate the poutput size with the current hardcoded action channels +UserInputMapper::UserInputMapper() { + registerStandardDevice(); + assignDefaulActionScales(); + createActionNames(); +} + +UserInputMapper::~UserInputMapper() { +} + + +bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ + proxy->_name += " (" + QString::number(deviceID) + ")"; + _registeredDevices[deviceID] = proxy; + return true; +} + +UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { + auto device = _registeredDevices.find(input.getDevice()); + if (device != _registeredDevices.end()) { + return (device->second); + } else { + return DeviceProxy::Pointer(); + } +} + +QString UserInputMapper::getDeviceName(uint16 deviceID) { + if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { + return _registeredDevices[deviceID]->_name; + } + return QString("unknown"); +} + + +void UserInputMapper::resetAllDeviceBindings() { + for (auto device : _registeredDevices) { + device.second->resetDeviceBindings(); + } +} + +void UserInputMapper::resetDevice(uint16 deviceID) { + auto device = _registeredDevices.find(deviceID); + if (device != _registeredDevices.end()) { + device->second->resetDeviceBindings(); + } +} + +int UserInputMapper::findDevice(QString name) { + for (auto device : _registeredDevices) { + if (device.second->_name.split(" (")[0] == name) { + return device.first; + } + } + return 0; +} + +QVector UserInputMapper::getDeviceNames() { + QVector result; + for (auto device : _registeredDevices) { + QString deviceName = device.second->_name.split(" (")[0]; + result << deviceName; + } + return result; +} + + +bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { + return addInputChannel(action, input, Input(), scale); +} + +bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { + // Check that the device is registered + if (!getDeviceProxy(input)) { + qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; + return false; + } + + auto inputChannel = InputChannel(input, modifier, action, scale); + + // Insert or replace the input to modifiers + if (inputChannel.hasModifier()) { + auto& modifiers = _inputToModifiersMap[input.getID()]; + modifiers.push_back(inputChannel._modifier); + std::sort(modifiers.begin(), modifiers.end()); + } + + // Now update the action To Inputs side of things + _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); + + return true; +} + +int UserInputMapper::addInputChannels(const InputChannels& channels) { + int nbAdded = 0; + for (auto& channel : channels) { + nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); + } + return nbAdded; +} + +bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { + // Remove from Input to Modifiers map + if (inputChannel.hasModifier()) { + _inputToModifiersMap.erase(inputChannel._input.getID()); + } + + // Remove from Action to Inputs map + std::pair ret; + ret = _actionToInputsMap.equal_range(inputChannel._action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + if (it->second == inputChannel) { + _actionToInputsMap.erase(it); + return true; + } + } + + return false; +} + +void UserInputMapper::removeAllInputChannels() { + _inputToModifiersMap.clear(); + _actionToInputsMap.clear(); +} + +void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { + QVector channels = getAllInputsForDevice(device); + for (auto& channel : channels) { + removeInputChannel(channel); + } +} + +void UserInputMapper::removeDevice(int device) { + removeAllInputChannelsForDevice((uint16) device); + _registeredDevices.erase(device); +} + +int UserInputMapper::getInputChannels(InputChannels& channels) const { + for (auto& channel : _actionToInputsMap) { + channels.push_back(channel.second); + } + + return _actionToInputsMap.size(); +} + +QVector UserInputMapper::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector channels; + for (InputChannel inputChannel : allChannels) { + if (inputChannel._input._device == device) { + channels.push_back(inputChannel); + } + } + + return channels; +} + +void UserInputMapper::update(float deltaTime) { + + // Reset the axis state for next loop + for (auto& channel : _actionStates) { + channel = 0.0f; + } + + for (auto& channel : _poseStates) { + channel = PoseValue(); + } + + int currentTimestamp = 0; + + for (auto& channelInput : _actionToInputsMap) { + auto& inputMapping = channelInput.second; + auto& inputID = inputMapping._input; + bool enabled = true; + + // Check if this input channel has modifiers and collect the possibilities + auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); + if (modifiersIt != _inputToModifiersMap.end()) { + Modifiers validModifiers; + bool isActiveModifier = false; + for (auto& modifier : modifiersIt->second) { + auto deviceProxy = getDeviceProxy(modifier); + if (deviceProxy->getButton(modifier, currentTimestamp)) { + validModifiers.push_back(modifier); + isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); + } + } + enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; + } + + // if enabled: default input or all modifiers on + if (enabled) { + auto deviceProxy = getDeviceProxy(inputID); + switch (inputMapping._input.getType()) { + case ChannelType::BUTTON: { + _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime + break; + } + case ChannelType::AXIS: { + _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); + break; + } + case ChannelType::POSE: { + if (!_poseStates[channelInput.first].isValid()) { + _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); + } + break; + } + default: { + break; //silence please + } + } + } else{ + // Channel input not enabled + enabled = false; + } + } + + // Scale all the channel step with the scale + static const float EPSILON = 0.01f; + for (auto i = 0; i < NUM_ACTIONS; i++) { + _actionStates[i] *= _actionScales[i]; + // Emit only on change, and emit when moving back to 0 + if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { + _lastActionStates[i] = _actionStates[i]; + emit actionEvent(i, _actionStates[i]); + } + // TODO: emit signal for pose changes + } +} + +QVector UserInputMapper::getAllActions() const { + QVector actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector inputChannels; + std::pair ret; + ret = _actionToInputsMap.equal_range(action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + inputChannels.append(it->second); + } + return inputChannels; +} + +int UserInputMapper::findAction(const QString& actionName) const { + auto actions = getAllActions(); + for (auto action : actions) { + if (getActionName(action) == actionName) { + return action; + } + } + // If the action isn't found, return -1 + return -1; +} + +QVector UserInputMapper::getActionNames() const { + QVector result; + for (auto i = 0; i < NUM_ACTIONS; i++) { + result << _actionNames[i]; + } + return result; +} + +void UserInputMapper::assignDefaulActionScales() { + _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit + _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit + _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit + _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit + _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit + _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit + _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit + _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit + _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit + _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit + _actionScales[BOOM_IN] = 0.5f; // .5m per unit + _actionScales[BOOM_OUT] = 0.5f; // .5m per unit + _actionScales[LEFT_HAND] = 1.0f; // default + _actionScales[RIGHT_HAND] = 1.0f; // default + _actionScales[LEFT_HAND_CLICK] = 1.0f; // on + _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on + _actionStates[SHIFT] = 1.0f; // on + _actionStates[ACTION1] = 1.0f; // default + _actionStates[ACTION2] = 1.0f; // default + _actionStates[TRANSLATE_X] = 1.0f; // default + _actionStates[TRANSLATE_Y] = 1.0f; // default + _actionStates[TRANSLATE_Z] = 1.0f; // default + _actionStates[ROLL] = 1.0f; // default + _actionStates[PITCH] = 1.0f; // default + _actionStates[YAW] = 1.0f; // default +} + +// This is only necessary as long as the actions are hardcoded +// Eventually you can just add the string when you add the action +void UserInputMapper::createActionNames() { + _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; + _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; + _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; + _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; + _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; + _actionNames[VERTICAL_UP] = "VERTICAL_UP"; + _actionNames[YAW_LEFT] = "YAW_LEFT"; + _actionNames[YAW_RIGHT] = "YAW_RIGHT"; + _actionNames[PITCH_DOWN] = "PITCH_DOWN"; + _actionNames[PITCH_UP] = "PITCH_UP"; + _actionNames[BOOM_IN] = "BOOM_IN"; + _actionNames[BOOM_OUT] = "BOOM_OUT"; + _actionNames[LEFT_HAND] = "LEFT_HAND"; + _actionNames[RIGHT_HAND] = "RIGHT_HAND"; + _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; + _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; + _actionNames[SHIFT] = "SHIFT"; + _actionNames[ACTION1] = "ACTION1"; + _actionNames[ACTION2] = "ACTION2"; + _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; + _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; + _actionNames[TRANSLATE_X] = "TranslateX"; + _actionNames[TRANSLATE_Y] = "TranslateY"; + _actionNames[TRANSLATE_Z] = "TranslateZ"; + _actionNames[ROLL] = "Roll"; + _actionNames[PITCH] = "Pitch"; + _actionNames[YAW] = "Yaw"; +} + +void UserInputMapper::registerStandardDevice() { + _standardController = std::make_shared(); + _standardController->registerToUserInputMapper(*this); +} + +float UserInputMapper::DeviceProxy::getValue(const Input& input, int timestamp) const { + switch (input.getType()) { + case UserInputMapper::ChannelType::BUTTON: + return getButton(input, timestamp) ? 1.0f : 0.0f; + + case UserInputMapper::ChannelType::AXIS: + return getAxis(input, timestamp); + + case UserInputMapper::ChannelType::POSE: + return getPose(input, timestamp)._valid ? 1.0f : 0.0f; + + default: + return 0.0f; + } +} diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h old mode 100755 new mode 100644 similarity index 96% rename from libraries/input-plugins/src/input-plugins/UserInputMapper.h rename to libraries/controllers/src/controllers/UserInputMapper.h index 304e74e8cc..2a42dfa1aa --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -146,44 +146,52 @@ public: // Actions are the output channels of the Mapper, that's what the InputChannel map to // For now the Actions are hardcoded, this is bad, but we will fix that in the near future enum Action { - LONGITUDINAL_BACKWARD = 0, - LONGITUDINAL_FORWARD, + TRANSLATE_X = 0, + TRANSLATE_Y, + TRANSLATE_Z, + ROTATE_X, PITCH = ROTATE_X, + ROTATE_Y, YAW = ROTATE_Y, + ROTATE_Z, ROLL = ROTATE_Z, - LATERAL_LEFT, - LATERAL_RIGHT, + TRANSLATE_CAMERA_Z, - VERTICAL_DOWN, - VERTICAL_UP, - - YAW_LEFT, - YAW_RIGHT, - - PITCH_DOWN, - PITCH_UP, - - BOOM_IN, - BOOM_OUT, - LEFT_HAND, RIGHT_HAND, LEFT_HAND_CLICK, RIGHT_HAND_CLICK, - SHIFT, - ACTION1, ACTION2, CONTEXT_MENU, TOGGLE_MUTE, - TranslateX, - TranslateY, - TranslateZ, - Roll, - Pitch, - Yaw, + SHIFT, + + // Biseced aliases for TRANSLATE_Z + LONGITUDINAL_BACKWARD, + LONGITUDINAL_FORWARD, + + // Biseced aliases for TRANSLATE_X + LATERAL_LEFT, + LATERAL_RIGHT, + + // Biseced aliases for TRANSLATE_Y + VERTICAL_DOWN, + VERTICAL_UP, + + // Biseced aliases for ROTATE_Y + YAW_LEFT, + YAW_RIGHT, + + // Biseced aliases for ROTATE_X + PITCH_DOWN, + PITCH_UP, + + // Biseced aliases for TRANSLATE_CAMERA_Z + BOOM_IN, + BOOM_OUT, NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index a826b4fd4d..1e889f2dda 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -12,7 +12,7 @@ #include #include "RouteBuilderProxy.h" -#include "../NewControllerScriptingInterface.h" +#include "../ScriptingInterface.h" #include "../Logging.h" namespace controller { diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 70495a9cfe..024faa7a4a 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -20,12 +20,14 @@ class QScriptValue; namespace controller { -class NewControllerScriptingInterface; +class ScriptingInterface; +// TODO migrate functionality to a MappingBuilder class and make the proxy defer to that +// (for easier use in both C++ and JS) class MappingBuilderProxy : public QObject { Q_OBJECT public: - MappingBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping) + MappingBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping) : _parent(parent), _mapping(mapping) { } Q_INVOKABLE QObject* from(const QJSValue& source); @@ -38,7 +40,7 @@ protected: QObject* from(const Endpoint::Pointer& source); friend class RouteBuilderProxy; - NewControllerScriptingInterface& _parent; + ScriptingInterface& _parent; Mapping::Pointer _mapping; }; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index e6b67e9ca6..50c3b534d1 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -12,7 +12,7 @@ #include #include "MappingBuilderProxy.h" -#include "../NewControllerScriptingInterface.h" +#include "../ScriptingInterface.h" #include "../Logging.h" namespace controller { diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 63cd106edb..d7cf024468 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -19,12 +19,14 @@ class QScriptValue; namespace controller { -class NewControllerScriptingInterface; +class ScriptingInterface; +// TODO migrate functionality to a RouteBuilder class and make the proxy defer to that +// (for easier use in both C++ and JS) class RouteBuilderProxy : public QObject { Q_OBJECT public: - RouteBuilderProxy(NewControllerScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route) + RouteBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route) : _parent(parent), _mapping(mapping), _route(route) { } Q_INVOKABLE void to(const QJSValue& destination); @@ -47,7 +49,7 @@ class RouteBuilderProxy : public QObject { Mapping::Pointer _mapping; Route::Pointer _route; - NewControllerScriptingInterface& _parent; + ScriptingInterface& _parent; }; } diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index bb90c04c95..4f2a525f07 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu model render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils) +link_hifi_libraries(shared gpu procedural model model-networking script-engine controllers render render-utils) target_bullet() diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 094a697012..4c33b2517a 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME input-plugins) setup_hifi_library() -link_hifi_libraries(shared plugins gpu render-utils) +link_hifi_libraries(shared plugins controllers) GroupSources("src/input-plugins") diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 2b16d905f5..b52dd3f658 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -22,7 +22,7 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), new SDL2Manager(), - new SixenseManager(), + //new SixenseManager(), //new ViveControllerManager(), nullptr }; diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index 684b9e80d5..e7a0124deb 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -9,13 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include - #include "Joystick.h" -#include "StandardControls.h" +#include +#include const float CONTROLLER_THRESHOLD = 0.3f; diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index 2a7a11d230..c6537acafe 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -20,8 +20,8 @@ #undef main #endif -#include "InputDevice.h" -#include "StandardControls.h" +#include +#include class Joystick : public QObject, public InputDevice { Q_OBJECT diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index e8a6131387..3c935cab26 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -16,7 +16,7 @@ #include -#include "InputDevice.h" +#include #include "InputPlugin.h" class QTouchEvent; diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/libraries/input-plugins/src/input-plugins/SDL2Manager.h index 23e3ee059f..fec6972591 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.h @@ -16,9 +16,9 @@ #include #endif -#include "InputPlugin.h" -#include "UserInputMapper.h" +#include +#include "InputPlugin.h" #include "Joystick.h" class SDL2Manager : public InputPlugin { diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 9fa7f84a86..53ee7f3c29 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -24,8 +24,9 @@ #endif +#include + #include "InputPlugin.h" -#include "InputDevice.h" class QLibrary; diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index 5cae8daaf4..bcc27d07ae 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -19,7 +19,7 @@ #include #include -#include "InputDevice.h" +#include #include "InputPlugin.h" #include #include diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index cfe0afd220..5e3d135034 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME script-engine) setup_hifi_library(Gui Network Script WebSockets Widgets) -link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities animation audio physics) +link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities controllers animation audio physics) diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h deleted file mode 100644 index d6a6b51b62..0000000000 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ /dev/null @@ -1,125 +0,0 @@ -// -// AbstractControllerScriptingInterface.h -// libraries/script-engine/src -// -// Created by Brad Hefta-Gaub on 12/17/13. -// Copyright 2013 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 -// - -#ifndef hifi_AbstractControllerScriptingInterface_h -#define hifi_AbstractControllerScriptingInterface_h - -#include - -#include -#include - -#include "HFActionEvent.h" -#include "KeyEvent.h" -#include "MouseEvent.h" -#include "SpatialEvent.h" -#include "TouchEvent.h" -#include "WheelEvent.h" - -class ScriptEngine; - -class AbstractInputController : public QObject { - Q_OBJECT - -public: - typedef unsigned int Key; - - virtual void update() = 0; - - virtual Key getKey() const = 0; - -public slots: - - virtual bool isActive() const = 0; - virtual glm::vec3 getAbsTranslation() const = 0; - virtual glm::quat getAbsRotation() const = 0; - virtual glm::vec3 getLocTranslation() const = 0; - virtual glm::quat getLocRotation() const = 0; - -signals: - void spatialEvent(const SpatialEvent& event); - -}; - -/// handles scripting of input controller commands from JS -class AbstractControllerScriptingInterface : public QObject { - Q_OBJECT - -public slots: - virtual void registerControllerTypes(ScriptEngine* engine) = 0; - - virtual bool isPrimaryButtonPressed() const = 0; - virtual glm::vec2 getPrimaryJoystickPosition() const = 0; - - virtual int getNumberOfButtons() const = 0; - virtual bool isButtonPressed(int buttonIndex) const = 0; - - virtual int getNumberOfTriggers() const = 0; - virtual float getTriggerValue(int triggerIndex) const = 0; - - virtual int getNumberOfJoysticks() const = 0; - virtual glm::vec2 getJoystickPosition(int joystickIndex) const = 0; - - virtual int getNumberOfSpatialControls() const = 0; - virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0; - virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0; - virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0; - virtual glm::quat getSpatialControlRawRotation(int controlIndex) const = 0; - - virtual void captureKeyEvents(const KeyEvent& event) = 0; - virtual void releaseKeyEvents(const KeyEvent& event) = 0; - - virtual void captureMouseEvents() = 0; - virtual void releaseMouseEvents() = 0; - - virtual void captureTouchEvents() = 0; - virtual void releaseTouchEvents() = 0; - - virtual void captureWheelEvents() = 0; - virtual void releaseWheelEvents() = 0; - - virtual void captureActionEvents() = 0; - virtual void releaseActionEvents() = 0; - - virtual void captureJoystick(int joystickIndex) = 0; - virtual void releaseJoystick(int joystickIndex) = 0; - - virtual glm::vec2 getViewportDimensions() const = 0; - - - virtual AbstractInputController* createInputController( const QString& category, const QString& tracker ) = 0; - -signals: - void keyPressEvent(const KeyEvent& event); - void keyReleaseEvent(const KeyEvent& event); - - void actionStartEvent(const HFActionEvent& event); - void actionEndEvent(const HFActionEvent& event); - - void backStartEvent(); - void backEndEvent(); - - void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0); - void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0); - void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0); - void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0); - - void touchBeginEvent(const TouchEvent& event); - void touchEndEvent(const TouchEvent& event); - void touchUpdateEvent(const TouchEvent& event); - - void wheelEvent(const WheelEvent& event); - - void actionEvent(int action, float state); - -}; - -#endif // hifi_AbstractControllerScriptingInterface_h diff --git a/libraries/script-engine/src/AbstractScriptingServicesInterface.h b/libraries/script-engine/src/AbstractScriptingServicesInterface.h index 9d24531b60..565a415f63 100644 --- a/libraries/script-engine/src/AbstractScriptingServicesInterface.h +++ b/libraries/script-engine/src/AbstractScriptingServicesInterface.h @@ -12,7 +12,10 @@ #ifndef hifi_AbstractScriptingServicesInterface_h #define hifi_AbstractScriptingServicesInterface_h -class AbstractControllerScriptingInterface; +namespace controller { + class ScriptingInterface; +} + class Transform; class ScriptEngine; class QThread; @@ -22,7 +25,7 @@ class AbstractScriptingServicesInterface { public: /// Returns the controller interface for the application - virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() = 0; + virtual controller::ScriptingInterface* getControllerScriptingInterface() = 0; /// Registers application specific services with a script engine. virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 76590f266b..40d4d664ce 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -76,16 +76,16 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { out = qobject_cast(object.toQObject()); } -QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { + +QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { return engine->newQObject(in); } -void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { - out = qobject_cast(object.toQObject()); +void inputControllerFromScriptValue(const QScriptValue &object, controller::InputController* &out) { + out = qobject_cast(object.toQObject()); } -ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, - AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) : +ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, controller::ScriptingInterface* controllerScriptingInterface, bool wantSignals) : _scriptContents(scriptContents), _isFinished(false), @@ -93,7 +93,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _isInitialized(false), _timerFunctionMap(), _wantSignals(wantSignals), - _controllerScriptingInterface(controllerScriptingInterface), _fileNameString(fileNameString), _quatLibrary(), _vec3Library(), @@ -301,7 +300,7 @@ void ScriptEngine::init() { globalObject().setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue); qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); - qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); + //qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); @@ -310,7 +309,7 @@ void ScriptEngine::init() { registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); - registerGlobalObject("Controller", _controllerScriptingInterface); +// registerGlobalObject("Controller", _controllerScriptingInterface); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); @@ -320,9 +319,9 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - if (_controllerScriptingInterface) { - _controllerScriptingInterface->registerControllerTypes(this); - } + //if (_controllerScriptingInterface) { + // _controllerScriptingInterface->registerControllerTypes(this); + //} } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1d3986143a..0108899571 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -26,8 +26,10 @@ #include #include #include +#include -#include "AbstractControllerScriptingInterface.h" + +#include "MouseEvent.h" #include "ArrayBufferClass.h" #include "AudioScriptingInterface.h" #include "Quat.h" @@ -53,7 +55,7 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""), - AbstractControllerScriptingInterface* controllerScriptingInterface = NULL, + controller::ScriptingInterface* controllerScriptingInterface = nullptr, bool wantSignals = true); ~ScriptEngine(); @@ -182,7 +184,6 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; Quat _quatLibrary; Vec3 _vec3Library; diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index 41d623a389..5f9bbd455e 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -8,17 +8,17 @@ import "./controls" Column { id: root - property var actions: NewControllers.Actions - property var standard: NewControllers.Standard + property var actions: Controllers.Actions + property var standard: Controllers.Standard property var testMapping: null property var xbox: null Component.onCompleted: { var patt = /^X360Controller/; - for (var prop in NewControllers.Hardware) { + for (var prop in Controllers.Hardware) { if(patt.test(prop)) { - root.xbox = NewControllers.Hardware[prop] + root.xbox = Controllers.Hardware[prop] break } } @@ -29,7 +29,7 @@ Column { Timer { interval: 50; running: true; repeat: true onTriggered: { - NewControllers.update(); + Controllers.update(); } } @@ -38,7 +38,7 @@ Column { Button { text: "Default Mapping" onClicked: { - var mapping = NewControllers.newMapping("Default"); + var mapping = Controllers.newMapping("Default"); mapping.from(xbox.A).to(standard.A); mapping.from(xbox.B).to(standard.B); mapping.from(xbox.X).to(standard.X); @@ -59,7 +59,7 @@ Column { mapping.from(xbox.RX).to(standard.RX); mapping.from(xbox.LT).to(standard.LT); mapping.from(xbox.RT).to(standard.RT); - NewControllers.enableMapping("Default"); + Controllers.enableMapping("Default"); enabled = false; text = "Built" } @@ -68,16 +68,35 @@ Column { Button { text: "Build Mapping" onClicked: { - var mapping = NewControllers.newMapping(); + var mapping = Controllers.newMapping(); // Inverting a value mapping.from(xbox.RY).invert().to(standard.RY); // Assigning a value from a function mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); // Constrainting a value to -1, 0, or 1, with a deadzone mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); - mapping.join(standard.LB, standard.RB).pulse(0.5).to(actions.Yaw); + // change join to makeAxis + mapping.join(standard.LB, standard.RB).to(actions.Yaw); mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); + + // mapping.modifier(keyboard.Ctrl).scale(2.0) + +// mapping.from(keyboard.A).to(actions.TranslateLeft) +// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) + +// // First loopbacks +// // Then non-loopbacks by constraint level (number of inputs) +// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) + +// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) + + +// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) + + +// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) testMapping = mapping; enabled = false text = "Built" @@ -105,7 +124,7 @@ Column { Row { spacing: 8 ScrollingGraph { - controlId: NewControllers.Actions.Yaw + controlId: Controllers.Actions.Yaw label: "Yaw" min: -3.0 max: 3.0 @@ -113,7 +132,7 @@ Column { } ScrollingGraph { - controlId: NewControllers.Actions.YAW_LEFT + controlId: Controllers.Actions.YAW_LEFT label: "Yaw Left" min: -3.0 max: 3.0 @@ -121,7 +140,7 @@ Column { } ScrollingGraph { - controlId: NewControllers.Actions.YAW_RIGHT + controlId: Controllers.Actions.YAW_RIGHT label: "Yaw Right" min: -3.0 max: 3.0 diff --git a/tests/controllers/qml/controls/AnalogButton.qml b/tests/controllers/qml/controls/AnalogButton.qml index 26f91458ac..141c131063 100644 --- a/tests/controllers/qml/controls/AnalogButton.qml +++ b/tests/controllers/qml/controls/AnalogButton.qml @@ -13,7 +13,7 @@ Item { property color color: 'black' function update() { - value = NewControllers.getValue(controlId); + value = Controllers.getValue(controlId); canvas.requestPaint(); } diff --git a/tests/controllers/qml/controls/AnalogStick.qml b/tests/controllers/qml/controls/AnalogStick.qml index 8860aea49c..5d011411c9 100644 --- a/tests/controllers/qml/controls/AnalogStick.qml +++ b/tests/controllers/qml/controls/AnalogStick.qml @@ -15,8 +15,8 @@ Item { function update() { value = Qt.vector2d( - NewControllers.getValue(controlIds[0]), - NewControllers.getValue(controlIds[1]) + Controllers.getValue(controlIds[0]), + Controllers.getValue(controlIds[1]) ); canvas.requestPaint(); } diff --git a/tests/controllers/qml/controls/ScrollingGraph.qml b/tests/controllers/qml/controls/ScrollingGraph.qml index 69f919aaf1..471d142d27 100644 --- a/tests/controllers/qml/controls/ScrollingGraph.qml +++ b/tests/controllers/qml/controls/ScrollingGraph.qml @@ -22,7 +22,7 @@ Item { property string label: "" function update() { - value = NewControllers.getValue(controlId); + value = Controllers.getValue(controlId); canvas.requestPaint(); } diff --git a/tests/controllers/qml/controls/ToggleButton.qml b/tests/controllers/qml/controls/ToggleButton.qml index 9ef54f5971..46a7b4bdfd 100644 --- a/tests/controllers/qml/controls/ToggleButton.qml +++ b/tests/controllers/qml/controls/ToggleButton.qml @@ -15,7 +15,7 @@ Item { Timer { interval: 50; running: true; repeat: true onTriggered: { - root.value = NewControllers.getValue(root.controlId); + root.value = Controllers.getValue(root.controlId); canvas.requestPaint(); } } diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 7182862ff1..3a45195439 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -38,10 +38,10 @@ #include #include #include -#include +#include #include -#include +#include const QString& getQmlDir() { static QString dir; @@ -87,6 +87,22 @@ int main(int argc, char** argv) { } + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, [] { + static float last = secTimestampNow(); + float now = secTimestampNow(); + float delta = now - last; + last = now; + + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + inputPlugin->pluginUpdate(delta, false); + } + + auto userInputMapper = DependencyManager::get(); + userInputMapper->update(delta); + }); + timer.start(50); + { DependencyManager::set(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { @@ -98,17 +114,9 @@ int main(int argc, char** argv) { keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); } } - - //new PluginContainerProxy(); auto rootContext = engine.rootContext(); - - auto controllers = new NewControllerScriptingInterface(); - rootContext->setContextProperty("NewControllers", controllers); - QVariantMap map; - map.insert("Hardware", controllers->property("Hardware")); - map.insert("Actions", controllers->property("Actions")); - rootContext->setContextProperty("ControllerIds", map); + rootContext->setContextProperty("Controllers", new ScriptingInterface()); } engine.load(getQmlDir() + "main.qml"); app.exec(); From 4107f4ea9f877d971c1e6a94dbfebc22cf737bb9 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Oct 2015 15:32:10 -0700 Subject: [PATCH 028/232] guard sixense from updating before activated --- libraries/input-plugins/src/input-plugins/SixenseManager.cpp | 5 +++++ libraries/input-plugins/src/input-plugins/SixenseManager.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index e7a4feedd8..a20fe5fc4c 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -122,6 +122,7 @@ void SixenseManager::activate() { #endif loadSettings(); sixenseInit(); + _activated = true; #endif } @@ -138,6 +139,7 @@ void SixenseManager::deactivate() { #endif sixenseExit(); + _activated = false; #ifdef __APPLE__ delete _sixenseLibrary; @@ -157,6 +159,9 @@ void SixenseManager::setSixenseFilter(bool filter) { } void SixenseManager::update(float deltaTime, bool jointsCaptured) { + if (!_activated) { + return; + } #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 9fa7f84a86..ae843cf2c8 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -117,6 +117,9 @@ private: static const QString NAME; static const QString HYDRA_ID_STRING; + + bool _activated = false; + }; #endif // hifi_SixenseManager_h From e39219c2b5a7e2d52b4de105d736c90015998255 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 16:51:52 -0700 Subject: [PATCH 029/232] Fixes and PR comments --- .../scripts/controllerScriptingExamples.js | 38 +++++++++++++------ examples/mouseLook.js | 2 +- .../ControllerScriptingInterface.cpp | 19 +++++++++- .../scripting/ControllerScriptingInterface.h | 2 +- .../src/controllers/ScriptingInterface.cpp | 10 +++-- .../src/controllers/ScriptingInterface.h | 2 +- .../src/controllers/StandardControls.h | 5 +++ .../src/input-plugins/InputPlugin.cpp | 4 +- libraries/script-engine/src/ScriptEngine.cpp | 15 ++++---- libraries/script-engine/src/ScriptEngine.h | 1 + 10 files changed, 69 insertions(+), 29 deletions(-) diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index e678ff26eb..b3dc92029d 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -11,6 +11,16 @@ // Assumes you only have the default keyboard connected +function findAction(name) { + var actions = Controller.getAllActions(); + for (var i = 0; i < actions.length; i++) { + if (actions[i].actionName == name) { + return i; + } + } + // If the action isn't found, it will default to the first available action + return 0; +} var hydra = Controller.Hardware.Hydra2; @@ -46,44 +56,48 @@ Controller.resetAllDeviceBindings(); // Query all actions print("All Actions: \n" + Controller.getAllActions()); +var actionId = findAction("YAW_LEFT") + +print("Yaw Left action ID: " + actionId) + // Each action stores: // action: int representation of enum -print("Action 5 int: \n" + Controller.getAllActions()[5].action); +print("Action int: \n" + Controller.getAllActions()[actionId].action); // actionName: string representation of enum -print("Action 5 name: \n" + Controller.getAllActions()[5].actionName); +print("Action name: \n" + Controller.getAllActions()[actionId].actionName); // inputChannels: list of all inputchannels that control that action -print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n"); +print("Action input channels: \n" + Controller.getAllActions()[actionId].inputChannels + "\n"); // Each input channel stores: // action: Action that this InputChannel maps to -print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action); +print("Input channel action: \n" + Controller.getAllActions()[actionId].inputChannels[0].action); // scale: sensitivity of input -print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale); +print("Input channel scale: \n" + Controller.getAllActions()[actionId].inputChannels[0].scale); // input and modifier: Inputs -print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n"); +print("Input channel input and modifier: \n" + Controller.getAllActions()[actionId].inputChannels[0].input + "\n" + Controller.getAllActions()[actionId].inputChannels[0].modifier + "\n"); // Each Input stores: // device: device of input -print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device); +print("Input device: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.device); // channel: channel of input -print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel); +print("Input channel: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.channel); // type: type of input (Unknown, Button, Axis, Joint) -print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type); +print("Input type: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.type); // id: id of input -print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n"); +print("Input id: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.id + "\n"); // You can get the name of a device from its id -print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id)); +print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[actionId].inputChannels[0].input.id)); // You can also get all of a devices input channels print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n"); @@ -119,7 +133,7 @@ for (i = 0; i < availableInputs.length; i++) { // You can modify key bindings by using these avaiable inputs // This will replace e (up) with 6 -var e = Controller.getAllActions()[5].inputChannels[0]; +var e = Controller.getAllActions()[actionId].inputChannels[0]; Controller.removeInputChannel(e); e.input = availableInputs[6].input; Controller.addInputChannel(e); \ No newline at end of file diff --git a/examples/mouseLook.js b/examples/mouseLook.js index 880ec138c4..81bc9d2813 100644 --- a/examples/mouseLook.js +++ b/examples/mouseLook.js @@ -38,7 +38,7 @@ var mouseLook = (function () { keyboardID = 0; function onKeyPressEvent(event) { - if (event.text == 'M') { + if (event.text == 'm') { active = !active; updateMapping(); } diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 1a212fbea9..f1f7b08587 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "Application.h" #include "devices/MotionTracker.h" @@ -115,7 +116,7 @@ void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::Input inputPair.second = QString(object.property("inputName").toVariant().toString()); } -void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) { +void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) { qScriptRegisterSequenceMetaType >(engine); qScriptRegisterSequenceMetaType >(engine); qScriptRegisterSequenceMetaType >(engine); @@ -426,11 +427,25 @@ void ControllerScriptingInterface::releaseInputController(controller::InputContr } void ControllerScriptingInterface::update() { - controller::ScriptingInterface::update(); + static float last = secTimestampNow(); + float now = secTimestampNow(); + float delta = now - last; + last = now; + + for(auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { + if (inputPlugin->isActive()) { + inputPlugin->pluginUpdate(delta, false); + } + } + + auto userInputMapper = DependencyManager::get(); + userInputMapper->update(delta); for (auto entry : _inputControllers) { entry.second->update(); } + + controller::ScriptingInterface::update(); } QVector ControllerScriptingInterface::getAllActions() { diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 652bd640b1..25d9a523d3 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -88,7 +88,7 @@ public: Q_INVOKABLE QVector getActionNames() const; - virtual void registerControllerTypes(ScriptEngine* engine); + virtual void registerControllerTypes(QScriptEngine* engine); void emitKeyPressEvent(QKeyEvent* event); void emitKeyReleaseEvent(QKeyEvent* event); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 9d2cdfd2de..a843775dcc 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -357,7 +357,7 @@ namespace controller { } int ScriptingInterface::getNumberOfTriggers() const { - return 2; + return StandardCounts::TRIGGERS; } float ScriptingInterface::getTriggerValue(int triggerIndex) const { @@ -365,7 +365,7 @@ namespace controller { } int ScriptingInterface::getNumberOfJoysticks() const { - return 2; + return StandardCounts::ANALOG_STICKS; } glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const { @@ -382,22 +382,26 @@ namespace controller { } int ScriptingInterface::getNumberOfSpatialControls() const { - return 2; + return StandardCounts::POSES; } glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const { + // FIXME extract the position from the standard pose return vec3(); } glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const { + // FIXME extract the velocity from the standard pose return vec3(); } glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const { + // FIXME extract the normal from the standard pose return vec3(); } glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { + // FIXME extract the rotation from the standard pose return quat(); } } // namespace controllers diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 043e06a67f..0e0e38e055 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -99,7 +99,7 @@ namespace controller { public slots: virtual void update(); - //virtual void registerControllerTypes(ScriptEngine* engine) = 0; + virtual void registerControllerTypes(QScriptEngine* engine) = 0; private: friend class MappingBuilderProxy; diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index 505b4f85c7..0990e34224 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -56,4 +56,9 @@ namespace controller { NUM_STANDARD_POSES }; + enum StandardCounts { + TRIGGERS = 2, + ANALOG_STICKS = 2, + POSES = 2, // FIXME 3? if we want to expose the head? + }; } diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index b52dd3f658..227bd12e1b 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -22,8 +22,8 @@ InputPluginList getInputPlugins() { InputPlugin* PLUGIN_POOL[] = { new KeyboardMouseDevice(), new SDL2Manager(), - //new SixenseManager(), - //new ViveControllerManager(), + new SixenseManager(), + new ViveControllerManager(), nullptr }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 40d4d664ce..494f038a61 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -76,7 +76,6 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { out = qobject_cast(object.toQObject()); } - QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { return engine->newQObject(in); } @@ -85,7 +84,8 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu out = qobject_cast(object.toQObject()); } -ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, controller::ScriptingInterface* controllerScriptingInterface, bool wantSignals) : +ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, + controller::ScriptingInterface* controllerScriptingInterface, bool wantSignals) : _scriptContents(scriptContents), _isFinished(false), @@ -93,6 +93,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _isInitialized(false), _timerFunctionMap(), _wantSignals(wantSignals), + _controllerScriptingInterface(controllerScriptingInterface), _fileNameString(fileNameString), _quatLibrary(), _vec3Library(), @@ -300,7 +301,7 @@ void ScriptEngine::init() { globalObject().setProperty("AudioEffectOptions", audioEffectOptionsConstructorValue); qScriptRegisterMetaType(this, injectorToScriptValue, injectorFromScriptValue); - //qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); + qScriptRegisterMetaType(this, inputControllerToScriptValue, inputControllerFromScriptValue); qScriptRegisterMetaType(this, avatarDataToScriptValue, avatarDataFromScriptValue); qScriptRegisterMetaType(this, animationDetailsToScriptValue, animationDetailsFromScriptValue); qScriptRegisterMetaType(this, webSocketToScriptValue, webSocketFromScriptValue); @@ -309,7 +310,7 @@ void ScriptEngine::init() { registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); -// registerGlobalObject("Controller", _controllerScriptingInterface); + registerGlobalObject("Controller", _controllerScriptingInterface); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); @@ -319,9 +320,9 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - //if (_controllerScriptingInterface) { - // _controllerScriptingInterface->registerControllerTypes(this); - //} + if (_controllerScriptingInterface) { + _controllerScriptingInterface->registerControllerTypes(this); + } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 0108899571..642159339e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -184,6 +184,7 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); + controller::ScriptingInterface* _controllerScriptingInterface; QString _fileNameString; Quat _quatLibrary; Vec3 _vec3Library; From 10df0f2d8c097889c32789a37ea2467eedd74e86 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 17:07:10 -0700 Subject: [PATCH 030/232] Fix broken assignment-client build --- assignment-client/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index e543b3aa21..5b92dfba99 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,12 +1,12 @@ set(TARGET_NAME assignment-client) -setup_hifi_project(Core Gui Network Script Widgets WebSockets) +setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) # link in the shared libraries link_hifi_libraries( audio avatars octree environment gpu model fbx entities networking animation shared script-engine embedded-webserver - physics + controllers physics ) include_application_version() From 195045a4ec8a29c11ef65a1cecf433bcca756c98 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 17:20:30 -0700 Subject: [PATCH 031/232] Next time remember to hit build all before commiting --- tests/controllers/src/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 3a45195439..2056e34551 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -79,6 +79,12 @@ public: virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } }; +class MyControllerScriptingInterface : public controller::ScriptingInterface { +public: + virtual void registerControllerTypes(QScriptEngine* engine) {}; +}; + + int main(int argc, char** argv) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; @@ -116,7 +122,7 @@ int main(int argc, char** argv) { } //new PluginContainerProxy(); auto rootContext = engine.rootContext(); - rootContext->setContextProperty("Controllers", new ScriptingInterface()); + rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); } engine.load(getQmlDir() + "main.qml"); app.exec(); From 12e103c90c03c707c9c13601166e06ee99d17fb2 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Oct 2015 17:41:39 -0700 Subject: [PATCH 032/232] Get json creating the mappings from js --- examples/controllers/controllerMappings.js | 71 ++++++++ .../controllers/src/controllers/Filter.cpp | 46 ++++- .../controllers/src/controllers/Filter.h | 157 +++++++++--------- .../NewControllerScriptingInterface.cpp | 35 +++- .../NewControllerScriptingInterface.h | 1 + .../controllers/impl/RouteBuilderProxy.cpp | 28 +--- .../src/input-plugins/UserInputMapper.cpp | 46 ++++- .../src/input-plugins/UserInputMapper.h | 5 +- 8 files changed, 275 insertions(+), 114 deletions(-) create mode 100644 examples/controllers/controllerMappings.js diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js new file mode 100644 index 0000000000..45383886c2 --- /dev/null +++ b/examples/controllers/controllerMappings.js @@ -0,0 +1,71 @@ + +// +// controllerScriptingExamples.js +// examples +// +// Created by Sam Gondelman on 6/2/15 +// 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 +// + +// Assumes you only have the default keyboard connected + +myFirstMapping = function() { +return { + "name": "example mapping from Standard to actions", + "channels": [ { + "from": "Keyboard.A", + "filters": [ { + "type": "clamp", + "params": [0, 1], + } + ], + "to": "Actions.LONGITUDINAL_FORWARD", + }, { + "from": "Keyboard.Left", + "filters": [ { + "type": "clamp", + "params": [0, 1], + }, { + "type": "invert" + } + ], + "to": "Actions.LONGITUDINAL_BACKWARD", + }, { + "from": "Keyboard.C", + "filters": [ { + "type": "scale", + "params": [2.0], + } + ], + "to": "Actions.Yaw", + }, { + "from": "Keyboard.B", + "to": "Actions.Yaw" + } + ] +} +} + + +//Script.include('mapping-test0.json'); +var myFirstMappingJSON = myFirstMapping(); +print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); + +var mapping = NewControllers.parseMapping(JSON.stringify(myFirstMappingJSON)); + +Object.keys(Controller.Standard).forEach(function (input) { + print("Controller.Standard." + input + ":" + Controller.Standard[input]); +}); + +Object.keys(Controller.Hardware).forEach(function (deviceName) { + Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { + print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); + }); +}); + +Object.keys(Controller.Actions).forEach(function (actionName) { + print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]); +}); diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index b7175f1716..3e1079b984 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -44,7 +44,7 @@ Filter::Pointer Filter::parse(const QJsonObject& json) { return Filter::Pointer(); } -Filter::Factory::ClassEntry ScaleFilter::_factoryEntry; +ScaleFilter::FactoryEntryBuilder ScaleFilter::_factoryEntryBuilder; bool ScaleFilter::parseParameters(const QJsonArray& parameters) { if (parameters.size() > 1) { @@ -53,7 +53,39 @@ bool ScaleFilter::parseParameters(const QJsonArray& parameters) { return true; } -Filter::Factory::ClassEntry PulseFilter::_factoryEntry; +InvertFilter::FactoryEntryBuilder InvertFilter::_factoryEntryBuilder; + + +ClampFilter::FactoryEntryBuilder ClampFilter::_factoryEntryBuilder; + +bool ClampFilter::parseParameters(const QJsonArray& parameters) { + if (parameters.size() > 1) { + _min = parameters[0].toDouble(); + } + if (parameters.size() > 2) { + _max = parameters[1].toDouble(); + } + return true; +} + +DeadZoneFilter::FactoryEntryBuilder DeadZoneFilter::_factoryEntryBuilder; + +float DeadZoneFilter::apply(float value) const { + float scale = 1.0f / (1.0f - _min); + if (abs(value) < _min) { + return 0.0f; + } + return (value - _min) * scale; +} + +bool DeadZoneFilter::parseParameters(const QJsonArray& parameters) { + if (parameters.size() > 1) { + _min = parameters[0].toDouble(); + } + return true; +} + +PulseFilter::FactoryEntryBuilder PulseFilter::_factoryEntryBuilder; float PulseFilter::apply(float value) const { @@ -72,5 +104,13 @@ float PulseFilter::apply(float value) const { } bool PulseFilter::parseParameters(const QJsonArray& parameters) { - return false; + if (parameters.size() > 1) { + _interval = parameters[0].toDouble(); + } + return true; } + +ConstrainToIntegerFilter::FactoryEntryBuilder ConstrainToIntegerFilter::_factoryEntryBuilder; + +ConstrainToPositiveIntegerFilter::FactoryEntryBuilder ConstrainToPositiveIntegerFilter::_factoryEntryBuilder; + diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index 4d21966731..4d8c483b08 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -16,6 +16,8 @@ #include #include +#include + #include class QJsonObject; @@ -23,40 +25,7 @@ class QJsonArray; namespace controller { - /* - template class Factory { - public: - template class Entry { - public: - virtual T* create() = 0; - }; - - template class DefaultEntry{ - public: - T* create() { return new S(); } - }; - - using EntryMap = std::map>>; - - void registerEntry(const std::string& name, std::unique_ptr>& entry) { - if (entry) { - _entries[name] = entry; - } - } - - T* create(const std::string& name) const { - auto& entryIt = _entries.find(name); - if (entryIt != _entries.end()) { - return (*entryIt).second->create(); - } - return nullptr; - } - - protected: - EntryMap _entries; - }; - */ // Encapsulates part of a filter chain class Filter { public: @@ -67,36 +36,40 @@ namespace controller { using Lambda = std::function; // Factory features - virtual bool parseParameters(const QJsonArray& parameters) = 0; + virtual bool parseParameters(const QJsonArray& parameters) { return true; } class Factory { public: class Entry { public: - virtual Filter* create() = 0; - virtual const std::string& getName() const = 0; + virtual Filter* create() const = 0; + virtual const char* getName() const = 0; - Entry() = default; - virtual ~Entry() = default; + Entry() {}; + virtual ~Entry() {}; }; - template class ClassEntry { + template class ClassEntry : public Entry { public: - Filter* create() override { return (Filter*) new T(); } - const std::string& getName() const override { - return _name - }; + virtual Filter* create() const { return (Filter*) new T(); } + virtual const char* getName() const { return T::getName(); }; - ClassEntry() : _name(name) {}; + ClassEntry() {}; virtual ~ClassEntry() = default; - const std::string _name; + class Builder { + public: + Builder() { + std::shared_ptr classEntry(new ClassEntry()); + Filter::getFactory().registerEntry(classEntry); + } + }; }; using EntryMap = std::map>; - void registerEntry(const std::shared_ptr& entry) { + void registerEntry(std::shared_ptr& entry) { if (entry) { _entries.insert(EntryMap::value_type(entry->getName(), entry)); } @@ -122,8 +95,9 @@ namespace controller { } #define REGISTER_FILTER_CLASS(classEntry, className) \ - using FactoryEntry = Filter::Factory::ClassEntry;\ - static FactoryEntry _factoryEntry; + static const char* getName() { return className; } \ + using FactoryEntryBuilder = Filter::Factory::ClassEntry::Builder;\ + static FactoryEntryBuilder _factoryEntryBuilder; namespace controller { @@ -150,6 +124,7 @@ namespace controller { class ScaleFilter : public Filter { public: + REGISTER_FILTER_CLASS(ScaleFilter, "scale"); ScaleFilter() {} ScaleFilter(float scale): _scale(scale) {} @@ -158,38 +133,48 @@ namespace controller { } virtual bool parseParameters(const QJsonArray& parameters); - // static Filter::Factory::ClassEntry _factoryEntry; - REGISTER_FILTER_CLASS(ScaleFilter, "scale"); private: float _scale = 1.0f; }; - //class AbstractRangeFilter : public Filter { - //public: - // RangeFilter(float min, float max) : _min(min), _max(max) {} + class InvertFilter : public ScaleFilter { + public: + REGISTER_FILTER_CLASS(InvertFilter, "invert"); + InvertFilter() : ScaleFilter(-1.0f) {} + + virtual bool parseParameters(const QJsonArray& parameters) { return true; } - //protected: - // const float _min; - // const float _max; - //}; + private: + }; - ///* - //* Constrains will emit the input value on the first call, and every *interval* seconds, otherwise returns 0 - //*/ - //class PulseFilter : public Filter { - //public: - // PulseFilter(float interval); - // virtual float apply(float value) const override; + class ClampFilter : public Filter { + public: + REGISTER_FILTER_CLASS(ClampFilter, "clamp"); + ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; - //private: - // float _lastEmitTime{ -std::numeric_limits::max() }; - // const float _interval; - //}; + virtual float apply(float value) const override { + return glm::clamp(value, _min, _max); + } + virtual bool parseParameters(const QJsonArray& parameters) override; + protected: + float _min = 0.0f; + float _max = 1.0f; + }; + class DeadZoneFilter : public Filter { + public: + REGISTER_FILTER_CLASS(DeadZoneFilter, "deadZone"); + DeadZoneFilter(float min = 0.0) : _min(min) {}; + + virtual float apply(float value) const override; + virtual bool parseParameters(const QJsonArray& parameters) override; + protected: + float _min = 0.0f; + }; class PulseFilter : public Filter { public: - REGISTER_FILTER_CLASS(PulseFilter); + REGISTER_FILTER_CLASS(PulseFilter, "pulse"); PulseFilter() {} PulseFilter(float interval) : _interval(interval) {} @@ -199,15 +184,31 @@ namespace controller { virtual bool parseParameters(const QJsonArray& parameters); private: - mutable float _lastEmitTime{ -std::numeric_limits::max() }; + mutable float _lastEmitTime{ -::std::numeric_limits::max() }; float _interval = 1.0f; }; - ////class DeadzoneFilter : public AbstractRangeFilter { - ////public: - //// DeadzoneFilter(float min, float max = 1.0f); - //// virtual float apply(float newValue, float oldValue) override; - ////}; + class ConstrainToIntegerFilter : public Filter { + public: + REGISTER_FILTER_CLASS(ConstrainToIntegerFilter, "constrainToInteger"); + ConstrainToIntegerFilter() {}; + + virtual float apply(float value) const override { + return glm::sign(value); + } + protected: + }; + + class ConstrainToPositiveIntegerFilter : public Filter { + public: + REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger"); + ConstrainToPositiveIntegerFilter() {}; + + virtual float apply(float value) const override { + return (value <= 0.0f) ? 0.0f : 1.0f; + } + protected: + }; //class EasingFilter : public Filter { //public: @@ -236,12 +237,6 @@ namespace controller { // const float _exponent; //}; - //class ClampFilter : public RangeFilter { - //public: - // ClampFilter(float min = 0.0, float max = 1.0) : RangeFilter(min, max) {}; - // virtual float apply(float value) const override; - //}; - //class AbsFilter : public Filter { //public: // virtual float apply(float value) const override; diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp index c80e5fc721..baeae55128 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.cpp @@ -12,6 +12,9 @@ #include +#include +#include + #include #include #include @@ -22,8 +25,6 @@ #include "impl/MappingBuilderProxy.h" #include "Logging.h" -static const uint16_t ACTIONS_DEVICE = UserInputMapper::Input::INVALID_DEVICE - (uint16_t)1; - namespace controller { @@ -161,7 +162,7 @@ namespace controller { int actionNumber = 0; qCDebug(controllers) << "Setting up standard actions"; for (const auto& actionName : actionNames) { - UserInputMapper::Input actionInput(ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); + UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); // Expose the IDs to JS _actions.insert(sanatizeName(actionName), actionInput.getID()); @@ -182,6 +183,34 @@ namespace controller { return new MappingBuilderProxy(*this, mapping); } + QObject* NewControllerScriptingInterface::parseMapping(const QString& json) { + + QJsonObject obj; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8()); + // check validity of the document + if (!doc.isNull()) { + if (doc.isObject()) { + obj = doc.object(); + + auto mapping = std::make_shared("default"); + auto mappingBuilder = new MappingBuilderProxy(*this, mapping); + + mappingBuilder->parse(obj); + + _mappingsByName[mapping->_name] = mapping; + + } else { + qDebug() << "Mapping json Document is not an object" << endl; + } + } else { + qDebug() << "Invalid JSON...\n" << json << endl; + } + + return nullptr; + } + + Q_INVOKABLE QObject* newMapping(const QJsonObject& json); + void NewControllerScriptingInterface::enableMapping(const QString& mappingName, bool enable) { auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { diff --git a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h index bdc291bc03..784b8cf5bd 100644 --- a/libraries/controllers/src/controllers/NewControllerScriptingInterface.h +++ b/libraries/controllers/src/controllers/NewControllerScriptingInterface.h @@ -40,6 +40,7 @@ namespace controller { Q_INVOKABLE void update(); Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); + Q_INVOKABLE QObject* parseMapping(const QString& json); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index d606b52608..38efcfdb00 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -57,9 +57,7 @@ QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { QObject* RouteBuilderProxy::clamp(float min, float max) { - addFilter([=](float value) { - return glm::clamp(value, min, max); - }); + addFilter(Filter::Pointer(new ClampFilter(min, max))); return this; } @@ -69,40 +67,28 @@ QObject* RouteBuilderProxy::scale(float multiplier) { } QObject* RouteBuilderProxy::invert() { - return scale(-1.0f); + addFilter(Filter::Pointer(new InvertFilter())); + return this; } QObject* RouteBuilderProxy::deadZone(float min) { - assert(min < 1.0f); - float scale = 1.0f / (1.0f - min); - addFilter([=](float value) { - if (abs(value) < min) { - return 0.0f; - } - return (value - min) * scale; - }); - + addFilter(Filter::Pointer(new DeadZoneFilter(min))); return this; } QObject* RouteBuilderProxy::constrainToInteger() { - addFilter([=](float value) { - return glm::sign(value); - }); + addFilter(Filter::Pointer(new ConstrainToIntegerFilter())); return this; } QObject* RouteBuilderProxy::constrainToPositiveInteger() { - addFilter([=](float value) { - return (value <= 0.0f) ? 0.0f : 1.0f; - }); + addFilter(Filter::Pointer(new ConstrainToPositiveIntegerFilter())); return this; } QObject* RouteBuilderProxy::pulse(float interval) { - Filter::Pointer filter = std::make_shared(interval); - addFilter(filter); + addFilter(Filter::Pointer(new PulseFilter(interval))); return this; } diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index 325dc5dfe7..0f797e9ad0 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -12,10 +12,15 @@ #include "UserInputMapper.h" #include "StandardController.h" +#include +Q_DECLARE_LOGGING_CATEGORY(userInputMapper) +Q_LOGGING_CATEGORY(userInputMapper, "hifi.userInputMapper") + const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX); const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); -const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); +const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); +const uint16_t UserInputMapper::Input::ACTIONS_DEVICE = INVALID_DEVICE - (uint16)1; // Default contruct allocate the poutput size with the current hardcoded action channels UserInputMapper::UserInputMapper() { @@ -31,6 +36,7 @@ UserInputMapper::~UserInputMapper() { bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ proxy->_name += " (" + QString::number(deviceID) + ")"; _registeredDevices[deviceID] = proxy; + qCDebug(userInputMapper) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; return true; } @@ -65,12 +71,18 @@ void UserInputMapper::resetDevice(uint16 deviceID) { } int UserInputMapper::findDevice(QString name) const { + if (_standardDevice && (_standardDevice->getName() == name)) { + return getStandardDeviceID(); + } + for (auto device : _registeredDevices) { if (device.second->_name.split(" (")[0] == name) { return device.first; + } else if (device.second->_baseName == name) { + return device.first; } } - return 0; + return Input::INVALID_DEVICE; } QVector UserInputMapper::getDeviceNames() { @@ -94,10 +106,34 @@ UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName int deviceID = findDevice(deviceName); if (deviceID != Input::INVALID_DEVICE) { - // getAllInputsForDevice(deviceID); + const auto& deviceProxy = _registeredDevices.at(deviceID); + auto deviceInputs = deviceProxy->getAvailabeInputs(); + + for (auto input : deviceInputs) { + if (input.second == inputName) { + return input.first; + } + } + + qCDebug(userInputMapper) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; + + } else if (deviceName == "Actions") { + deviceID = Input::ACTIONS_DEVICE; + int actionNum = 0; + for (auto action : _actionNames) { + if (action == inputName) { + return Input(Input::ACTIONS_DEVICE, actionNum, ChannelType::AXIS); + } + actionNum++; + } + + qCDebug(userInputMapper) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions"; + + } else { + qCDebug(userInputMapper) << "Couldn\'t find InputDevice named <" << deviceName << ">"; } - - + } else { + qCDebug(userInputMapper) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; } return Input(); diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index b7b105df5e..fae77cca92 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -85,6 +85,7 @@ public: static const uint16 INVALID_DEVICE; static const uint16 INVALID_CHANNEL; static const uint16 INVALID_TYPE; + static const uint16 ACTIONS_DEVICE; }; @@ -119,9 +120,11 @@ public: class DeviceProxy { public: - DeviceProxy(QString name) { _name = name; } + DeviceProxy(QString name) : _baseName(name), _name(name) {} + const QString& getBaseName() const { return _baseName; } const QString& getName() const { return _name; } + QString _baseName; QString _name; ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; From 46e40ed032ab7a40fe6832b9b97f15f56ffd3bb4 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 14 Oct 2015 18:52:19 -0700 Subject: [PATCH 033/232] work on device names --- .../src/input-plugins/SixenseManager.cpp | 9 ++++++--- .../src/input-plugins/UserInputMapper.cpp | 18 ++++++++++++++++-- .../src/input-plugins/UserInputMapper.h | 4 ++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index a20fe5fc4c..a99e04ff13 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -159,9 +159,12 @@ void SixenseManager::setSixenseFilter(bool filter) { } void SixenseManager::update(float deltaTime, bool jointsCaptured) { - if (!_activated) { - return; - } + // FIXME - Some of the code in update() will crash if you haven't actually activated the + // plugin. But we want register with the UserInputMapper if we don't call this. + // We need to clean this up. + //if (!_activated) { + // return; + //} #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp index c29acc09af..a30eac5c2a 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.cpp @@ -28,8 +28,22 @@ UserInputMapper::~UserInputMapper() { } -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ - proxy->_name += " (" + QString::number(deviceID) + ")"; +int UserInputMapper::recordDeviceOfType(const QString& deviceName) { + if (!_deviceCounts.contains(deviceName)) { + _deviceCounts[deviceName] = 0; + } + _deviceCounts[deviceName] += 1; + + return _deviceCounts[deviceName]; +} + +bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) { + int numberOfType = recordDeviceOfType(proxy->_name); + + if (numberOfType > 1) { + proxy->_name += QString::number(numberOfType); + } + _registeredDevices[deviceID] = proxy; return true; } diff --git a/libraries/input-plugins/src/input-plugins/UserInputMapper.h b/libraries/input-plugins/src/input-plugins/UserInputMapper.h index 304e74e8cc..5a535b6a3c 100755 --- a/libraries/input-plugins/src/input-plugins/UserInputMapper.h +++ b/libraries/input-plugins/src/input-plugins/UserInputMapper.h @@ -133,6 +133,7 @@ public: }; // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } + bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); bool registerStandardDevice(const DeviceProxy::Pointer& device) { _standardDevice = device; return true; } DeviceProxy::Pointer getDeviceProxy(const Input& input); @@ -285,6 +286,9 @@ protected: std::vector _poseStates = std::vector(NUM_ACTIONS); glm::mat4 _sensorToWorldMat; + + int recordDeviceOfType(const QString& deviceName); + QHash _deviceCounts; }; Q_DECLARE_METATYPE(UserInputMapper::InputPair) From 4f8e2c9f6b30b91f2f9b2b998043ef12e0155c18 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Oct 2015 13:18:04 -0700 Subject: [PATCH 034/232] Fix JSON format errors --- interface/resources/controllers/mapping-test0.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/resources/controllers/mapping-test0.json b/interface/resources/controllers/mapping-test0.json index c52b03be92..d6a1de5313 100644 --- a/interface/resources/controllers/mapping-test0.json +++ b/interface/resources/controllers/mapping-test0.json @@ -5,29 +5,29 @@ "filters": [ { "type": "clamp", "min": 0, - "max": 1, + "max": 1 } ], - "to": "Actions.Forward", + "to": "Actions.Forward" }, { "from": "Standard.LY", "filters": [ { "type": "clamp", "min": -1, - "max": 0, + "max": 0 }, { "type": "invert" } ], - "to": "Actions.Backward", + "to": "Actions.Backward" }, { "from": "Standard.LX", "filters": [ { "type": "scale", - "scale": 2.0, + "scale": 2.0 } ], - "to": "Actions.Yaw", + "to": "Actions.Yaw" }, { "from": "Standard.A", "to": "Actions.Action0" From eff8c28a28a5c4df273538b0cf4364f3946937d6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Oct 2015 14:04:55 -0700 Subject: [PATCH 035/232] Fixing access to hardware devices --- .../src/controllers/ScriptingInterface.cpp | 69 ++++++++++++------- .../src/controllers/ScriptingInterface.h | 1 + 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index a843775dcc..002814853f 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -111,31 +111,6 @@ namespace controller { ScriptingInterface::ScriptingInterface() { auto userInputMapper = DependencyManager::get(); - auto devices = userInputMapper->getDevices(); - for (const auto& deviceMapping : devices) { - auto device = deviceMapping.second.get(); - auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); - qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; - // Expose the IDs to JS - _hardware.insert(deviceName, createDeviceMap(device)); - - // Create the endpoints - for (const auto& inputMapping : device->getAvailabeInputs()) { - const auto& input = inputMapping.first; - // Ignore aliases - if (_endpoints.count(input)) { - continue; - } - _endpoints[input] = std::make_shared([=] { - auto deviceProxy = userInputMapper->getDeviceProxy(input); - if (!deviceProxy) { - return 0.0f; - } - return deviceProxy->getValue(input, 0); - }); - } - } - qCDebug(controllers) << "Setting up standard controller abstraction"; auto standardDevice = userInputMapper->getStandardDevice(); // Expose the IDs to JS @@ -150,6 +125,7 @@ namespace controller { _endpoints[standardInput] = std::make_shared(standardInput); } + // FIXME allow custom user actions? auto actionNames = userInputMapper->getActionNames(); int actionNumber = 0; qCDebug(controllers) << "Setting up standard actions"; @@ -164,6 +140,8 @@ namespace controller { // FIXME action endpoints need to accumulate values, and have them cleared at each frame _endpoints[actionInput] = std::make_shared(); } + + updateMaps(); } QObject* ScriptingInterface::newMapping(const QString& mappingName) { @@ -231,6 +209,12 @@ namespace controller { void ScriptingInterface::update() { auto userInputMapper = DependencyManager::get(); + static auto deviceNames = userInputMapper->getDeviceNames(); + + if (deviceNames != userInputMapper->getDeviceNames()) { + updateMaps(); + deviceNames = userInputMapper->getDeviceNames(); + } _overrideValues.clear(); EndpointSet readEndpoints; @@ -404,6 +388,41 @@ namespace controller { // FIXME extract the rotation from the standard pose return quat(); } + + void ScriptingInterface::updateMaps() { + auto userInputMapper = DependencyManager::get(); + auto devices = userInputMapper->getDevices(); + QSet foundDevices; + for (const auto& deviceMapping : devices) { + auto device = deviceMapping.second.get(); + auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); + qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; + foundDevices.insert(device->getName()); + if (_hardware.contains(deviceName)) { + continue; + } + + // Expose the IDs to JS + _hardware.insert(deviceName, createDeviceMap(device)); + + // Create the endpoints + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + // Ignore aliases + if (_endpoints.count(input)) { + continue; + } + _endpoints[input] = std::make_shared([=] { + auto deviceProxy = userInputMapper->getDeviceProxy(input); + if (!deviceProxy) { + return 0.0f; + } + return deviceProxy->getValue(input, 0); + }); + } + } + } + } // namespace controllers //var mapping = Controller.newMapping(); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 0e0e38e055..d90a47cf12 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -100,6 +100,7 @@ namespace controller { public slots: virtual void update(); virtual void registerControllerTypes(QScriptEngine* engine) = 0; + virtual void updateMaps(); private: friend class MappingBuilderProxy; From 416df1c44c7f1c181ab7fd81e66f5888a3bfb4a2 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 14:21:08 -0700 Subject: [PATCH 036/232] Fixing the mac build --- libraries/controllers/src/controllers/Filter.h | 2 +- .../src/controllers/impl/RouteBuilderProxy.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index 4d8c483b08..876f57c97d 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -76,7 +76,7 @@ namespace controller { } Filter* create(const std::string& name) const { - auto& entryIt = _entries.find(name); + const auto& entryIt = _entries.find(name); if (entryIt != _entries.end()) { return (*entryIt).second->create(); } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index aeef081b5f..dcf2b30f66 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -121,12 +121,12 @@ QObject* RouteBuilderProxy::filters(const QJsonValue& json) { } void RouteBuilderProxy::to(const QJsonValue& json) { - if (json.isString()) { - - return to(_parent.endpointFor(_parent.inputFor(json.toString()))); + if (json.isString()) { + + return to(_parent.endpointFor(_parent.inputFor(json.toString()))); } else if (json.isObject()) { // Endpoint is defined as an object, we expect a js function then - return to(nullptr); + //return to((Endpoint*) nullptr); } } From 249efa383ea41d534d802426fb966a4a5b17cfe9 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 14:49:22 -0700 Subject: [PATCH 037/232] MErging maybe finally ?????? --- .../src/controllers/UserInputMapper.cpp | 171 ------------------ 1 file changed, 171 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 2d03fbe58b..b62e2e0e8f 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -93,177 +93,6 @@ QVector UserInputMapper::getDeviceNames() { } UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { -/*======= - -// Default contruct allocate the poutput size with the current hardcoded action channels -UserInputMapper::UserInputMapper() { - registerStandardDevice(); - assignDefaulActionScales(); - createActionNames(); -} - -UserInputMapper::~UserInputMapper() { -} - - -int UserInputMapper::recordDeviceOfType(const QString& deviceName) { - if (!_deviceCounts.contains(deviceName)) { - _deviceCounts[deviceName] = 0; - } - _deviceCounts[deviceName] += 1; - - return _deviceCounts[deviceName]; -} - -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) { - int numberOfType = recordDeviceOfType(proxy->_name); - - if (numberOfType > 1) { - proxy->_name += QString::number(numberOfType); - } - - _registeredDevices[deviceID] = proxy; - return true; -} - -UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { - auto device = _registeredDevices.find(input.getDevice()); - if (device != _registeredDevices.end()) { - return (device->second); - } else { - return DeviceProxy::Pointer(); - } -} - -QString UserInputMapper::getDeviceName(uint16 deviceID) { - if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->_name; - } - return QString("unknown"); -} - - -void UserInputMapper::resetAllDeviceBindings() { - for (auto device : _registeredDevices) { - device.second->resetDeviceBindings(); - } -} - -void UserInputMapper::resetDevice(uint16 deviceID) { - auto device = _registeredDevices.find(deviceID); - if (device != _registeredDevices.end()) { - device->second->resetDeviceBindings(); - } -} - -int UserInputMapper::findDevice(QString name) { - for (auto device : _registeredDevices) { - if (device.second->_name.split(" (")[0] == name) { - return device.first; - } - } - return 0; -} - -QVector UserInputMapper::getDeviceNames() { - QVector result; - for (auto device : _registeredDevices) { - QString deviceName = device.second->_name.split(" (")[0]; - result << deviceName; - } - return result; -} - - -bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { - return addInputChannel(action, input, Input(), scale); -} - -bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { - // Check that the device is registered - if (!getDeviceProxy(input)) { - qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; - return false; - } - - auto inputChannel = InputChannel(input, modifier, action, scale); - - // Insert or replace the input to modifiers - if (inputChannel.hasModifier()) { - auto& modifiers = _inputToModifiersMap[input.getID()]; - modifiers.push_back(inputChannel._modifier); - std::sort(modifiers.begin(), modifiers.end()); - } - - // Now update the action To Inputs side of things - _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); - - return true; -} - -int UserInputMapper::addInputChannels(const InputChannels& channels) { - int nbAdded = 0; - for (auto& channel : channels) { - nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); - } - return nbAdded; -} - -bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { - // Remove from Input to Modifiers map - if (inputChannel.hasModifier()) { - _inputToModifiersMap.erase(inputChannel._input.getID()); - } - - // Remove from Action to Inputs map - std::pair ret; - ret = _actionToInputsMap.equal_range(inputChannel._action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - if (it->second == inputChannel) { - _actionToInputsMap.erase(it); - return true; - } - } - - return false; -} - -void UserInputMapper::removeAllInputChannels() { - _inputToModifiersMap.clear(); - _actionToInputsMap.clear(); -} - -void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { - QVector channels = getAllInputsForDevice(device); - for (auto& channel : channels) { - removeInputChannel(channel); - } -} - -void UserInputMapper::removeDevice(int device) { - removeAllInputChannelsForDevice((uint16) device); - _registeredDevices.erase(device); -} - -int UserInputMapper::getInputChannels(InputChannels& channels) const { - for (auto& channel : _actionToInputsMap) { - channels.push_back(channel.second); - } - - return _actionToInputsMap.size(); -} - -QVector UserInputMapper::getAllInputsForDevice(uint16 device) { - InputChannels allChannels; - getInputChannels(allChannels); - - QVector channels; - for (InputChannel inputChannel : allChannels) { - if (inputChannel._input._device == device) { - channels.push_back(inputChannel); - } - } ->>>>>>> 80cffdb764d3faa5516c8b0eb0a49d84cc395416*/ // Split the full input name as such: deviceName.inputName auto names = inputName.split('.'); From 1b03b6867c075a1c4b4430a0a682a8a4d9fb62aa Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 15:01:34 -0700 Subject: [PATCH 038/232] Fixing the include file names... --- libraries/controllers/src/controllers/Filter.cpp | 4 ++-- .../controllers/src/controllers/impl/MappingBuilderProxy.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index 3e1079b984..43317fd62d 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -11,8 +11,8 @@ #include #include -#include -#include +#include +#include #include "SharedUtil.h" diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index b347d8b7c6..fe7a5c24af 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -11,8 +11,9 @@ #include #include -#include -#include +#include +#include + #include "RouteBuilderProxy.h" #include "../ScriptingInterface.h" From 63ad9ae19837c2d829a42a63cb99a108d82350d8 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 15:02:55 -0700 Subject: [PATCH 039/232] Fixing the include file names... --- .../controllers/src/controllers/impl/RouteBuilderProxy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index dcf2b30f66..033e94daa0 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -9,8 +9,8 @@ #include -#include -#include +#include +#include #include From 43d7fe491ec4720df6976be638a41b9620534860 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 17:53:53 -0700 Subject: [PATCH 040/232] wiring the actions --- examples/controllers/controllerMappings.js | 4 +++- .../src/controllers/ScriptingInterface.cpp | 23 ++++++++++++++++++- .../src/controllers/UserInputMapper.h | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index e4ef0270ab..959dc1b0ca 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -14,7 +14,7 @@ myFirstMapping = function() { return { - "name": "example mapping from Standard to actions", + "name": "example", "channels": [ { "from": "Keyboard.A", "filters": [ { @@ -56,6 +56,8 @@ print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); +Controller.enableMapping("example"); + Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index d0c40fe3c0..e91e627f16 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -94,6 +94,25 @@ namespace controller { Endpoint::Pointer _second; }; + class ActionEndpoint : public Endpoint { + public: + ActionEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1)) + : Endpoint(id) { + } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { + + _currentValue = newValue; + if (!(_id == UserInputMapper::Input::INVALID_INPUT)) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->setActionState(UserInputMapper::Action(_id.getChannel()), newValue); + } + } + + private: + float _currentValue{ 0.0f }; + }; QRegularExpression ScriptingInterface::SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; @@ -139,7 +158,8 @@ namespace controller { // Create the endpoints // FIXME action endpoints need to accumulate values, and have them cleared at each frame - _endpoints[actionInput] = std::make_shared(); + // _endpoints[actionInput] = std::make_shared(); + _endpoints[actionInput] = std::make_shared(); } updateMaps(); @@ -171,6 +191,7 @@ namespace controller { _mappingsByName[mapping->_name] = mapping; + return mappingBuilder; } else { qDebug() << "Mapping json Document is not an object" << endl; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index c795442296..117fd163bf 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -214,6 +214,8 @@ public: QVector getActionNames() const; void assignDefaulActionScales(); + void setActionState(Action action, float value) { _actionStates[action] = value; } + // Add input channel to the mapper and check that all the used channels are registered. // Return true if theinput channel is created correctly, false either bool addInputChannel(Action action, const Input& input, float scale = 1.0f); From f25cc93936229efc139bd4e5b318cb22643353b5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 16 Oct 2015 10:48:36 -0700 Subject: [PATCH 041/232] Initial prototype of exposing anim vars to javascript. --- interface/src/avatar/MyAvatar.h | 12 +++ libraries/animation/src/AnimVariant.h | 6 ++ libraries/animation/src/AnimVariantMap.cpp | 91 ++++++++++++++++++++++ libraries/animation/src/Rig.cpp | 14 ++++ libraries/animation/src/Rig.h | 6 ++ 5 files changed, 129 insertions(+) create mode 100644 libraries/animation/src/AnimVariantMap.cpp diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 02c9f53082..372c0848bb 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -106,6 +106,18 @@ public: Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); void clearJointAnimationPriorities(); + // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. + // The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty) + // propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided. + // The animStateDictionaryOut can be a different object than animStateDictionaryIn. Any properties set in animStateDictionaryOut + // will override those of the internal animation machinery. + // The animStateDictionaryIn may be shared among multiple handlers, and thus may contain additional properties specified when + // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut) + // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. + // It is not specified in what order multiple handlers are called. + Q_INVOKABLE void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _rig->addAnimationStateHandler(handler, propertiesList); } + // Removes a handler previously added by addAnimationStateHandler. + Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } // get/set avatar data void saveData(); diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index cb886cd369..b30a04e6bd 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "AnimationLogging.h" class AnimVariant { @@ -158,6 +159,11 @@ public: bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } + // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. + QScriptValue animVariantMapToScriptValue(QScriptEngine* engine); + // Side-effect us with the value of object's own properties. (No inherited properties.) + void animVariantMapFromScriptValue(const QScriptValue& object); + #ifdef NDEBUG void dump() const { qCDebug(animation) << "AnimVariantMap ="; diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp new file mode 100644 index 0000000000..f626e46c0f --- /dev/null +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -0,0 +1,91 @@ +// +// AnimVariantMap.cpp +// library/animation +// +// Created by Howard Stearns on 10/15/15. +// Copyright (c) 2015 High Fidelity, Inc. All rights reserved. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include "AnimVariant.h" + +QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) { + QScriptValue target = engine->newObject(); + for (auto& pair : _map) { + switch (pair.second.getType()) { + case AnimVariant::Type::Bool: + target.setProperty(pair.first, pair.second.getBool()); + break; + case AnimVariant::Type::Int: + target.setProperty(pair.first, pair.second.getInt()); + break; + case AnimVariant::Type::Float: + target.setProperty(pair.first, pair.second.getFloat()); + break; + case AnimVariant::Type::String: + target.setProperty(pair.first, pair.second.getString()); + break; + case AnimVariant::Type::Vec3: + target.setProperty(pair.first, vec3toScriptValue(engine, pair.second.getVec3())); + break; + case AnimVariant::Type::Quat: + target.setProperty(pair.first, quatToScriptValue(engine, pair.second.getQuat())); + break; + default: + // Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now. + assert("AnimVariant::Type" == "valid"); + } + } + return target; +} +void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { + // POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types. + // Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here, + // we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if + // the type is not known. One problem with that is that there is no checking that two different script use the same name differently. + QScriptValueIterator property(source); + // Note: QScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain. + while (property.hasNext()) { + property.next(); + QScriptValue value = property.value(); + if (value.isBool()) { + set(property.name(), value.toBool()); + continue; + } else if (value.isString()) { + set(property.name(), value.toString()); + continue; + } else if (value.isNumber()) { + int asInteger = value.toInt32(); + float asFloat = value.toNumber(); + if (asInteger == asFloat) { + set(property.name(), asInteger); + } else { + set(property.name(), asFloat); + } + continue; + } else if (value.isObject()) { + QScriptValue x = value.property("x"); + if (x.isNumber()) { + QScriptValue y = value.property("y"); + if (y.isNumber()) { + QScriptValue z = value.property("z"); + if (z.isNumber()) { + QScriptValue w = value.property("w"); + if (w.isNumber()) { + set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber())); + } else { + set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber())); + } + continue; + } + } + } + } + qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name(); + } +} diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 340c09060a..bf5edb6300 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -566,6 +566,20 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { return; } + if (_stateHandlers.isValid()) { + // TODO: iterate multiple handlers, but with one shared arg. + // TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) + // TODO: check QScriptEngine::hasUncaughtException() + // TODO: call asynchronously (through a signal on script), so that each script is single threaded, and so we never block here. + // This will require inboundMaps to be kept in the list of per-handler data. + QScriptEngine* engine = _stateHandlers.engine(); + QScriptValue outboundMap = _animVars.animVariantMapToScriptValue(engine); + QScriptValueList args; + args << outboundMap; + QScriptValue inboundMap = _stateHandlers.call(QScriptValue(), args); + _animVars.animVariantMapFromScriptValue(inboundMap); + //qCDebug(animation) << _animVars.lookup("foo", QString("not set")); + } // evaluate the animation AnimNode::Triggers triggersOut; AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 6d9f7b4688..0d74c3b956 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -37,6 +37,7 @@ #define __hifi__Rig__ #include +#include #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS @@ -198,6 +199,8 @@ public: AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) + void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _stateHandlers = handler; } + void removeAnimationStateHandler(QScriptValue handler) { _stateHandlers = QScriptValue(); } bool getModelOffset(glm::vec3& modelOffsetOut) const; @@ -238,6 +241,9 @@ public: RigRole _state = RigRole::Idle; float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; + +private: + QScriptValue _stateHandlers {}; }; #endif /* defined(__hifi__Rig__) */ From f0034844e7b5ba938b7234239f2f2683f99fd917 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Oct 2015 12:24:12 -0700 Subject: [PATCH 042/232] Actions working from the Controller layer --- examples/controllers/controllerMappings.js | 39 ++++++++----------- interface/src/Application.cpp | 2 +- .../src/controllers/ScriptingInterface.cpp | 6 +-- .../src/controllers/UserInputMapper.cpp | 23 ++++++----- .../src/controllers/UserInputMapper.h | 6 ++- 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index 959dc1b0ca..1007e7bf22 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -15,32 +15,26 @@ myFirstMapping = function() { return { "name": "example", - "channels": [ { - "from": "Keyboard.A", - "filters": [ { - "type": "clamp", - "params": [0, 1], - } - ], - "to": "Actions.LONGITUDINAL_FORWARD", - }, { - "from": "Keyboard.Left", - "filters": [ { - "type": "clamp", - "params": [0, 1], - }, { - "type": "invert" - } - ], - "to": "Actions.LONGITUDINAL_BACKWARD", - }, { - "from": "Keyboard.C", + "channels": [ + { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, + + { "from": "Keyboard.Left", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "to": "Actions.LATERAL_RIGHT" }, + + { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, + { + "from": "Standard.LX", "filters": [ { "type": "scale", "params": [2.0], } ], - "to": "Actions.Yaw", + "to": "Actions.LATERAL_LEFT", }, { "from": "Keyboard.B", "to": "Actions.Yaw" @@ -57,7 +51,7 @@ print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); Controller.enableMapping("example"); - +/* Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); @@ -71,3 +65,4 @@ Object.keys(Controller.Hardware).forEach(function (deviceName) { Object.keys(Controller.Actions).forEach(function (actionName) { print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]); }); +*/ \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e0c296e61..c01e89c6d9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2692,7 +2692,7 @@ void Application::update(float deltaTime) { auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); - userInputMapper->update(deltaTime); + // userInputMapper->update(deltaTime); // This needs to go after userInputMapper->update() because of the keyboard bool jointsCaptured = false; diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index e91e627f16..a72f24e651 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -103,10 +103,10 @@ namespace controller { virtual float value() override { return _currentValue; } virtual void apply(float newValue, float oldValue, const Pointer& source) override { - _currentValue = newValue; + _currentValue += newValue; if (!(_id == UserInputMapper::Input::INVALID_INPUT)) { auto userInputMapper = DependencyManager::get(); - userInputMapper->setActionState(UserInputMapper::Action(_id.getChannel()), newValue); + userInputMapper->deltaActionState(UserInputMapper::Action(_id.getChannel()), newValue); } } @@ -159,7 +159,7 @@ namespace controller { // Create the endpoints // FIXME action endpoints need to accumulate values, and have them cleared at each frame // _endpoints[actionInput] = std::make_shared(); - _endpoints[actionInput] = std::make_shared(); + _endpoints[actionInput] = std::make_shared(actionInput); } updateMaps(); diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index b62e2e0e8f..daa7c78640 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -295,7 +295,12 @@ void UserInputMapper::update(float deltaTime) { // Scale all the channel step with the scale static const float EPSILON = 0.01f; for (auto i = 0; i < NUM_ACTIONS; i++) { + if (_externalActionStates[i] != 0) { + _actionStates[i] += _externalActionStates[i]; + _externalActionStates[i] = 0.0f; + } _actionStates[i] *= _actionScales[i]; + // Emit only on change, and emit when moving back to 0 if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { _lastActionStates[i] = _actionStates[i]; @@ -359,15 +364,15 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[RIGHT_HAND] = 1.0f; // default _actionScales[LEFT_HAND_CLICK] = 1.0f; // on _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on - _actionStates[SHIFT] = 1.0f; // on - _actionStates[ACTION1] = 1.0f; // default - _actionStates[ACTION2] = 1.0f; // default - _actionStates[TRANSLATE_X] = 1.0f; // default - _actionStates[TRANSLATE_Y] = 1.0f; // default - _actionStates[TRANSLATE_Z] = 1.0f; // default - _actionStates[ROLL] = 1.0f; // default - _actionStates[PITCH] = 1.0f; // default - _actionStates[YAW] = 1.0f; // default + _actionScales[SHIFT] = 1.0f; // on + _actionScales[ACTION1] = 1.0f; // default + _actionScales[ACTION2] = 1.0f; // default + _actionScales[TRANSLATE_X] = 1.0f; // default + _actionScales[TRANSLATE_Y] = 1.0f; // default + _actionScales[TRANSLATE_Z] = 1.0f; // default + _actionScales[ROLL] = 1.0f; // default + _actionScales[PITCH] = 1.0f; // default + _actionScales[YAW] = 1.0f; // default } // This is only necessary as long as the actions are hardcoded diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 117fd163bf..7e0832b55a 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -138,7 +138,7 @@ public: uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); - bool registerStandardDevice(const DeviceProxy::Pointer& device) { _standardDevice = device; return true; } + bool registerStandardDevice(const DeviceProxy::Pointer& device) { _standardDevice = device; _registeredDevices[getStandardDeviceID()] = device; return true; } DeviceProxy::Pointer getDeviceProxy(const Input& input); QString getDeviceName(uint16 deviceID); QVector getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } @@ -214,7 +214,8 @@ public: QVector getActionNames() const; void assignDefaulActionScales(); - void setActionState(Action action, float value) { _actionStates[action] = value; } + void setActionState(Action action, float value) { _externalActionStates[action] = value; } + void deltaActionState(Action action, float delta) { _externalActionStates[action] += delta; } // Add input channel to the mapper and check that all the used channels are registered. // Return true if theinput channel is created correctly, false either @@ -297,6 +298,7 @@ protected: ActionToInputsMap _actionToInputsMap; std::vector _actionStates = std::vector(NUM_ACTIONS, 0.0f); + std::vector _externalActionStates = std::vector(NUM_ACTIONS, 0.0f); std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); std::vector _lastActionStates = std::vector(NUM_ACTIONS, 0.0f); std::vector _poseStates = std::vector(NUM_ACTIONS); From 0fc04ab2972083df9daabe4fae6b14b0c01c2464 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Oct 2015 14:45:38 -0700 Subject: [PATCH 043/232] Fixing the review comments and setting the StandardDevice of USerINputMapper in the registeredDevices just like any other, only the ID is special --- .../src/controllers/ScriptingInterface.cpp | 14 ++++++-------- .../src/controllers/UserInputMapper.cpp | 12 ++++++++---- .../controllers/src/controllers/UserInputMapper.h | 11 +++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index a72f24e651..abb9544892 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -26,7 +26,7 @@ namespace controller { class VirtualEndpoint : public Endpoint { public: - VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1)) + VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT) : Endpoint(id) { } @@ -41,7 +41,7 @@ namespace controller { class JSEndpoint : public Endpoint { public: JSEndpoint(const QJSValue& callable) - : Endpoint(UserInputMapper::Input(-1)), _callable(callable) {} + : Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) {} virtual float value() { float result = (float)_callable.call().toNumber();; @@ -59,7 +59,7 @@ namespace controller { class ScriptEndpoint : public Endpoint { public: ScriptEndpoint(const QScriptValue& callable) - : Endpoint(UserInputMapper::Input(-1)), _callable(callable) { + : Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) { } virtual float value() { @@ -78,7 +78,7 @@ namespace controller { class CompositeEndpoint : public Endpoint, Endpoint::Pair { public: CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) - : Endpoint(UserInputMapper::Input(-1)), Pair(first, second) { } + : Endpoint(UserInputMapper::Input(UserInputMapper::Input::INVALID_INPUT)), Pair(first, second) { } virtual float value() { float result = first->value() * -1.0 + second->value(); @@ -96,7 +96,7 @@ namespace controller { class ActionEndpoint : public Endpoint { public: - ActionEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input(-1)) + ActionEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT) : Endpoint(id) { } @@ -156,9 +156,7 @@ namespace controller { QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); _actions.insert(cleanActionName, actionInput.getID()); - // Create the endpoints - // FIXME action endpoints need to accumulate values, and have them cleared at each frame - // _endpoints[actionInput] = std::make_shared(); + // Create the action endpoints _endpoints[actionInput] = std::make_shared(actionInput); } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index daa7c78640..0d0ae7d85f 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -19,6 +19,7 @@ const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice( const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); const uint16_t UserInputMapper::Input::ACTIONS_DEVICE = INVALID_DEVICE - (uint16)1; +const uint16_t UserInputMapper::Input::STANDARD_DEVICE = 0; // Default contruct allocate the poutput size with the current hardcoded action channels UserInputMapper::UserInputMapper() { @@ -38,6 +39,13 @@ bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer return true; } +bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { + device->_name = "Standard"; // Just to make sure + _registeredDevices[getStandardDeviceID()] = device; + return true; +} + + UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { auto device = _registeredDevices.find(input.getDevice()); if (device != _registeredDevices.end()) { @@ -69,10 +77,6 @@ void UserInputMapper::resetDevice(uint16 deviceID) { } int UserInputMapper::findDevice(QString name) const { - if (_standardDevice && (_standardDevice->getName() == name)) { - return getStandardDeviceID(); - } - for (auto device : _registeredDevices) { if (device.second->_name.split(" (")[0] == name) { return device.first; diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 7e0832b55a..8b466d79c9 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -86,6 +86,7 @@ public: static const uint16 INVALID_CHANNEL; static const uint16 INVALID_TYPE; static const uint16 ACTIONS_DEVICE; + static const uint16 STANDARD_DEVICE; }; @@ -138,7 +139,7 @@ public: uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); - bool registerStandardDevice(const DeviceProxy::Pointer& device) { _standardDevice = device; _registeredDevices[getStandardDeviceID()] = device; return true; } + bool registerStandardDevice(const DeviceProxy::Pointer& device); DeviceProxy::Pointer getDeviceProxy(const Input& input); QString getDeviceName(uint16 deviceID); QVector getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } @@ -275,8 +276,8 @@ public: typedef std::map DevicesMap; DevicesMap getDevices() { return _registeredDevices; } - uint16 getStandardDeviceID() const { return _standardDeviceID; } - DeviceProxy::Pointer getStandardDevice() { return _standardDevice; } + uint16 getStandardDeviceID() const { return Input::STANDARD_DEVICE; } + DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } signals: void actionEvent(int action, float state); @@ -284,12 +285,10 @@ signals: protected: void registerStandardDevice(); - uint16 _standardDeviceID = 0; - DeviceProxy::Pointer _standardDevice; StandardControllerPointer _standardController; DevicesMap _registeredDevices; - uint16 _nextFreeDeviceID = 1; + uint16 _nextFreeDeviceID = Input::STANDARD_DEVICE + 1; typedef std::map InputToMoModifiersMap; InputToMoModifiersMap _inputToModifiersMap; From 770282ad8849afdf0b5e7f87acaf89ce1f5ae54d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 16 Oct 2015 15:22:43 -0700 Subject: [PATCH 044/232] Javascript methods to getAvatars listing and getAvatar from id. --- interface/src/avatar/AvatarManager.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 277e931419..50e585d72c 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -51,6 +51,10 @@ public: Q_INVOKABLE void setLocalLights(const QVector& localLights); Q_INVOKABLE QVector getLocalLights() const; + // Currently, your own avatar will be included as the null avatar id. + Q_INVOKABLE QVector getAvatars() const { return _avatarHash.keys().toVector(); } + // Minor Bug: A bogus avatarID answers your own avatar. + Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID) const { return _avatarHash[avatarID].get(); } void getObjectsToDelete(VectorOfMotionStates& motionStates); void getObjectsToAdd(VectorOfMotionStates& motionStates); From 6d3d29b6b7707d4d5de7ffff70285b58903866a3 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Oct 2015 15:24:44 -0700 Subject: [PATCH 045/232] Adding a new mapping --- examples/controllers/controllerMappings.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index 1007e7bf22..c39ece4bc1 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -43,6 +43,15 @@ return { } } +mySecondMapping = function() { +return { + "name": "example2", + "channels": [ + { "from": "Standard.LY", "to": "Actions.TRANSLATE_Z" }, + { "from": "Standard.LX", "to": "Actions.YAW" }, + ] +} +} //Script.include('mapping-test0.json'); var myFirstMappingJSON = myFirstMapping(); @@ -51,6 +60,14 @@ print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); Controller.enableMapping("example"); + +var myFirstMappingJSON = myFirstMapping(); +print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); + +var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); + +Controller.enableMapping("example"); + /* Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); From 38a967ac5493ba53940f8b768a0808d22f77b0f2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 16 Oct 2015 16:28:11 -0700 Subject: [PATCH 046/232] Allow compiler after someone broke things. --- libraries/animation/src/AnimVariant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index b30a04e6bd..0a91c82d80 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -18,6 +18,7 @@ #include #include #include "AnimationLogging.h" +#include "StreamUtils.h" class AnimVariant { public: From 1dcf03d61e7627f17389eeba635920693a79e248 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 16 Oct 2015 17:15:46 -0700 Subject: [PATCH 047/232] Put standard 'makeinput' functions on the base class --- .../src/controllers/InputDevice.cpp | 25 +++++++++++++++++++ .../controllers/src/controllers/InputDevice.h | 10 +++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index 351d5b6d1d..4b2376d32a 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -57,3 +57,28 @@ UserInputMapper::PoseValue InputDevice::getPose(int channel) const { return UserInputMapper::PoseValue(); } } + +UserInputMapper::Input InputDevice::makeInput(controller::StandardButtonChannel button) { + return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +} + +UserInputMapper::Input InputDevice::makeInput(controller::StandardAxisChannel axis) { + return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +} + +UserInputMapper::Input InputDevice::makeInput(controller::StandardPoseChannel pose) { + return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); +} + +UserInputMapper::InputPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) { + return UserInputMapper::InputPair(makeInput(button), name); +} + +UserInputMapper::InputPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) { + return UserInputMapper::InputPair(makeInput(axis), name); +} + +UserInputMapper::InputPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) { + return UserInputMapper::InputPair(makeInput(pose), name); +} + diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 4dbb141832..66f7addc58 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -11,6 +11,7 @@ #pragma once #include "UserInputMapper.h" +#include "StandardControls.h" // Event types for each controller const unsigned int CONTROLLER_0_EVENT = 1500U; @@ -33,7 +34,7 @@ public: UserInputMapper::PoseValue getPose(int channel) const; virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) {}; // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas @@ -49,10 +50,17 @@ public: static bool getLowVelocityFilter() { return _lowVelocityFilter; }; + UserInputMapper::Input makeInput(controller::StandardButtonChannel button); + UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); + UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); + UserInputMapper::InputPair makePair(controller::StandardButtonChannel button, const QString& name); + UserInputMapper::InputPair makePair(controller::StandardAxisChannel button, const QString& name); + UserInputMapper::InputPair makePair(controller::StandardPoseChannel button, const QString& name); public slots: static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; protected: + int _deviceID = 0; QString _name; From db0fa6b8edfdd9571f68b21a3a176d0f76627ad7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 16 Oct 2015 17:18:15 -0700 Subject: [PATCH 048/232] Update hydra mappings and test code --- examples/tests/controllerInterfaceTest.js | 30 +++ interface/resources/controllers/hydra.json | 28 +++ interface/resources/controllers/xbox.json | 29 +++ .../src/input-plugins/Joystick.cpp | 19 -- .../src/input-plugins/Joystick.h | 4 - .../src/input-plugins/SixenseManager.cpp | 176 +++++------------- .../src/input-plugins/SixenseManager.h | 25 +-- tests/controllers/qml/Hydra.qml | 35 ++++ tests/controllers/qml/Xbox.qml | 16 +- tests/controllers/qml/content.qml | 33 ++-- .../controllers/qml/controls/AnalogStick.qml | 5 + tests/controllers/qml/hydra/HydraButtons.qml | 18 ++ tests/controllers/qml/hydra/HydraStick.qml | 91 +++++++++ tests/controllers/qml/hydra/hydra.png | Bin 0 -> 21273 bytes tests/controllers/src/main.cpp | 12 +- 15 files changed, 321 insertions(+), 200 deletions(-) create mode 100644 examples/tests/controllerInterfaceTest.js create mode 100644 interface/resources/controllers/hydra.json create mode 100644 interface/resources/controllers/xbox.json create mode 100644 tests/controllers/qml/Hydra.qml create mode 100644 tests/controllers/qml/hydra/HydraButtons.qml create mode 100644 tests/controllers/qml/hydra/HydraStick.qml create mode 100644 tests/controllers/qml/hydra/hydra.png diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js new file mode 100644 index 0000000000..fa8cf48b9b --- /dev/null +++ b/examples/tests/controllerInterfaceTest.js @@ -0,0 +1,30 @@ +ControllerTest = function() { + + print("Actions"); + for (var prop in Controller.Actions) { + print("\t" + prop); + } + print("Standard"); + for (var prop in Controller.Standard) { + print("\t" + prop); + } + print("Hardware"); + for (var prop in Controller.Hardware) { + print("\t" + prop); + for (var prop2 in Controller.Hardware[prop]) { + print("\t\t" + prop2); + } + } + print("Done"); + + var that = this; + Script.scriptEnding.connect(function() { + that.onCleanup(); + }); +} + +ControllerTest.prototype.onCleanup = function() { +} + + +new ControllerTest(); \ No newline at end of file diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json new file mode 100644 index 0000000000..25c8db61cb --- /dev/null +++ b/interface/resources/controllers/hydra.json @@ -0,0 +1,28 @@ +{ + "name": "Hydra to Standard", + "channels": [ + { "from": "Hydra.LY", "to": "Standard.LY" }, + { "from": "Hydra.LX", "to": "Standard.LX" }, + { "from": "Hydra.LT", "to": "Standard.LT" }, + { "from": "Hydra.RY", "to": "Standard.RY" }, + { "from": "Hydra.RX", "to": "Standard.RX" }, + { "from": "Hydra.RT", "to": "Standard.RT" }, + + { "from": "Hydra.LB", "to": "Standard.LB" }, + { "from": "Hydra.LS", "to": "Standard.LS" }, + { "from": "Hydra.RB", "to": "Standard.RB" }, + { "from": "Hydra.RS", "to": "Standard.RS" }, + + { "from": "Hydra.L0", "to": "Standard.Back" }, + { "from": "Hydra.L1", "to": "Standard.DL" }, + { "from": "Hydra.L2", "to": "Standard.DD" }, + { "from": "Hydra.L3", "to": "Standard.DR" }, + { "from": "Hydra.L4", "to": "Standard.DU" }, + + { "from": "Hydra.R0", "to": "Standard.Start" }, + { "from": "Hydra.R1", "to": "Standard.X" }, + { "from": "Hydra.R2", "to": "Standard.A" }, + { "from": "Hydra.R3", "to": "Standard.B" }, + { "from": "Hydra.R4", "to": "Standard.Y" } + ] +} diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json new file mode 100644 index 0000000000..bf96320707 --- /dev/null +++ b/interface/resources/controllers/xbox.json @@ -0,0 +1,29 @@ +{ + "name": "XBox to Standard", + "channels": [ + { "from": "XBox.LY", "to": "Standard.LY" }, + { "from": "XBox.LX", "to": "Standard.LX" }, + { "from": "XBox.LT", "to": "Standard.LT" }, + { "from": "XBox.LB", "to": "Standard.LB" }, + { "from": "XBox.LS", "to": "Standard.LS" }, + + { "from": "XBox.RY", "to": "Standard.RY" }, + { "from": "XBox.RX", "to": "Standard.RX" }, + { "from": "XBox.RT", "to": "Standard.RT" }, + { "from": "XBox.RB", "to": "Standard.RB" }, + { "from": "XBox.RS", "to": "Standard.RS" }, + + { "from": "XBox.Back", "to": "Standard.Back" }, + { "from": "XBox.Start", "to": "Standard.Start" }, + + { "from": "XBox.DU", "to": "Standard.DU" }, + { "from": "XBox.DD", "to": "Standard.DD" }, + { "from": "XBox.DL", "to": "Standard.DL" }, + { "from": "XBox.DR", "to": "Standard.DR" }, + + { "from": "XBox.A", "to": "Standard.A" }, + { "from": "XBox.B", "to": "Standard.B" }, + { "from": "XBox.X", "to": "Standard.X" }, + { "from": "XBox.Y", "to": "Standard.Y" } + ] +} diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index e7a0124deb..aa5bbbba07 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -143,22 +143,3 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { mapper.registerDevice(_deviceID, proxy); } -void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { -#ifdef HAVE_SDL2 - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - -#endif -} - -UserInputMapper::Input Joystick::makeInput(controller::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input Joystick::makeInput(controller::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} - diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index c6537acafe..8e4cdb365f 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -37,16 +37,12 @@ public: // Device functions virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; Joystick() : InputDevice("Joystick") {} ~Joystick(); - UserInputMapper::Input makeInput(controller::StandardButtonChannel button); - UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); - #ifdef HAVE_SDL2 Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); #endif diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index a99e04ff13..9d3d06ecb7 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -123,6 +123,8 @@ void SixenseManager::activate() { loadSettings(); sixenseInit(); _activated = true; + auto userInputMapper = DependencyManager::get(); + registerToUserInputMapper(*userInputMapper); #endif } @@ -176,23 +178,13 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { auto userInputMapper = DependencyManager::get(); if (sixenseGetNumActiveControllers() == 0) { - if (_hydrasConnected) { - qCDebug(inputplugins, "hydra disconnected"); - } - _hydrasConnected = false; - if (_deviceID != 0) { - userInputMapper->removeDevice(_deviceID); - _deviceID = 0; - _poseStateMap.clear(); - } + _poseStateMap.clear(); return; } PerformanceTimer perfTimer("sixense"); if (!_hydrasConnected) { _hydrasConnected = true; - registerToUserInputMapper(*userInputMapper); - assignDefaultInputMapping(*userInputMapper); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); } @@ -226,12 +218,15 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); position *= METERS_PER_MILLIMETER; - + bool left = i == 0; + using namespace controller; // Check to see if this hand/controller is on the base const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) { - handleButtonEvent(data->buttons, numActiveControllers - 1); - handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); + handleButtonEvent(data->buttons, left); + _axisStateMap[left ? LX : RX] = data->joystick_x; + _axisStateMap[left ? LY : RY] = data->joystick_y; + _axisStateMap[left ? LT : RT] = data->trigger; if (!jointsCaptured) { // Rotation of Palm @@ -241,13 +236,8 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { _poseStateMap.clear(); } } else { - _poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue(); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = UserInputMapper::PoseValue(); } - -// // Read controller buttons and joystick into the hand -// palm->setControllerButtons(data->buttons); -// palm->setTrigger(data->trigger); -// palm->setJoystick(data->joystick_x, data->joystick_y); } if (numActiveControllers == 2) { @@ -367,39 +357,35 @@ void SixenseManager::focusOutEvent() { _buttonPressedMap.clear(); }; -void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) { - _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f; - _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f; - _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f; - _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f; - _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger; +void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { } -void SixenseManager::handleButtonEvent(unsigned int buttons, int index) { +void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { + using namespace controller; if (buttons & BUTTON_0) { - _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel()); + _buttonPressedMap.insert(left ? BACK : START); } if (buttons & BUTTON_1) { - _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel()); + _buttonPressedMap.insert(left ? DL : X); } if (buttons & BUTTON_2) { - _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel()); + _buttonPressedMap.insert(left ? DD : A); } if (buttons & BUTTON_3) { - _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel()); + _buttonPressedMap.insert(left ? DR : B); } if (buttons & BUTTON_4) { - _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel()); + _buttonPressedMap.insert(left ? DU : Y); } if (buttons & BUTTON_FWD) { - _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel()); + _buttonPressedMap.insert(left ? LB : RB); } if (buttons & BUTTON_TRIGGER) { - _buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel()); + _buttonPressedMap.insert(left ? LS : RS); } } -void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) { +void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left) { #ifdef HAVE_SIXENSE // From ABOVE the sixense coordinate frame looks like this: // @@ -443,7 +429,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers // and how they fit into the hand in their relaxed state. This offset is a quarter turn about // the sixense's z-axis, with its direction different for the two hands: - float sign = (index == 0) ? 1.0f : -1.0f; + float sign = left ? 1.0f : -1.0f; const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z); // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation @@ -458,104 +444,46 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int // TODO: find a shortcut with fewer rotations. rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; - _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = + UserInputMapper::PoseValue(position, rotation); #endif // HAVE_SIXENSE } void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { // Grab the current free device ID _deviceID = mapper.getFreeDeviceID(); - auto proxy = std::make_shared(_name); proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { + using namespace controller; + proxy->getAvailabeInputs = [this]() -> QVector { QVector availableInputs; - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press")); - + availableInputs.append(UserInputMapper::InputPair(makeInput(BACK), "L0")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DL), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DD), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DR), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(DU), "L4")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(START), "R0")); + availableInputs.append(UserInputMapper::InputPair(makeInput(X), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(A), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(B), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(Y), "R4")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RB), "RB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RS), "RS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT")); return availableInputs; }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; mapper.registerDevice(_deviceID, proxy); } -void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BUTTON_MOVE_SPEED = 1.0f; - const float BOOM_SPEED = 0.1f; - - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED); - - // Buttons - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED); - - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0)); - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1)); - - mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); - - mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); - - // TODO find a mechanism to allow users to navigate the context menu via - mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(BUTTON_0, 0)); - mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(BUTTON_0, 1)); - -} - // virtual void SixenseManager::saveSettings() const { Settings settings; @@ -580,15 +508,3 @@ void SixenseManager::loadSettings() { } settings.endGroup(); } - -UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) { - return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) { - return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); -} - -UserInputMapper::Input SixenseManager::makeInput(JointChannel joint) { - return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE); -} diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 4558f5d268..2e7dd3223d 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -25,6 +25,7 @@ #endif #include +#include #include "InputPlugin.h" @@ -44,19 +45,6 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public InputPlugin, public InputDevice { Q_OBJECT public: - enum JoystickAxisChannel { - AXIS_Y_POS = 1U << 0, - AXIS_Y_NEG = 1U << 3, - AXIS_X_POS = 1U << 4, - AXIS_X_NEG = 1U << 5, - BACK_TRIGGER = 1U << 6, - }; - - enum JointChannel { - LEFT_HAND = 0, - RIGHT_HAND, - }; - SixenseManager(); // Plugin functions @@ -73,14 +61,9 @@ public: // Device functions virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; - UserInputMapper::Input makeInput(unsigned int button, int index); - UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); - UserInputMapper::Input makeInput(JointChannel joint); - virtual void saveSettings() const override; virtual void loadSettings() override; @@ -88,9 +71,9 @@ public slots: void setSixenseFilter(bool filter); private: - void handleButtonEvent(unsigned int buttons, int index); - void handleAxisEvent(float x, float y, float trigger, int index); - void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index); + void handleButtonEvent(unsigned int buttons, bool left); + void handleAxisEvent(float x, float y, float trigger, bool left); + void handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left); void updateCalibration(void* controllers); diff --git a/tests/controllers/qml/Hydra.qml b/tests/controllers/qml/Hydra.qml new file mode 100644 index 0000000000..5ef47c4d83 --- /dev/null +++ b/tests/controllers/qml/Hydra.qml @@ -0,0 +1,35 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./hydra" +import "./controls" + +Item { + id: root + width: 480 + height: width * 3.0 / 4.0 + property var device + property real scale: width / 480 + property real rightOffset: (width / 2) * scale + + Image { + anchors.fill: parent + source: "hydra/hydra.png" + + HydraStick { + leftStick: true + scale: root.scale + device: root.device + } + + + HydraStick { + leftStick: false + scale: root.scale + device: root.device + } + + } +} diff --git a/tests/controllers/qml/Xbox.qml b/tests/controllers/qml/Xbox.qml index ae66081162..bc9acd5a43 100644 --- a/tests/controllers/qml/Xbox.qml +++ b/tests/controllers/qml/Xbox.qml @@ -8,14 +8,20 @@ import "./controls" Item { id: root - + property real aspect: 300.0 / 215.0 + width: 300 + height: width / aspect property var device - - property real scale: 1.0 - width: 300 * scale - height: 215 * scale + property string label: "" + property real scale: width / 300.0 Image { + Text { + anchors.left: parent.left + anchors.top: parent.top + text: root.label + visible: root.label != "" + } anchors.fill: parent source: "xbox/xbox360-controller-md.png" diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index 5f9bbd455e..4beda82df3 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -10,16 +10,24 @@ Column { id: root property var actions: Controllers.Actions property var standard: Controllers.Standard + property var hydra: null property var testMapping: null property var xbox: null Component.onCompleted: { - var patt = /^X360Controller/; + var xboxRegex = /^X360Controller/; + var hydraRegex = /^Hydra/; for (var prop in Controllers.Hardware) { - if(patt.test(prop)) { + if(xboxRegex.test(prop)) { root.xbox = Controllers.Hardware[prop] - break + print("found xbox") + continue + } + if (hydraRegex.test(prop)) { + root.hydra = Controllers.Hardware[prop] + print("found hydra") + continue } } } @@ -79,23 +87,15 @@ Column { mapping.join(standard.LB, standard.RB).to(actions.Yaw); mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); - // mapping.modifier(keyboard.Ctrl).scale(2.0) - // mapping.from(keyboard.A).to(actions.TranslateLeft) // mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) // mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) - // // First loopbacks // // Then non-loopbacks by constraint level (number of inputs) // mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) - // mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) - - // mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) - - // mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) testMapping = mapping; enabled = false @@ -114,12 +114,19 @@ Column { } } + Row { + Xbox { device: root.standard; label: "Standard"; width: 360 } + } + Row { spacing: 8 - Xbox { device: root.xbox } - Xbox { device: root.standard } + Xbox { device: root.xbox; label: "XBox"; width: 360 } } + Row { + spacing: 8 + Hydra { device: root.hydra; width: 360 } + } Row { spacing: 8 diff --git a/tests/controllers/qml/controls/AnalogStick.qml b/tests/controllers/qml/controls/AnalogStick.qml index 5d011411c9..4e8ceb5736 100644 --- a/tests/controllers/qml/controls/AnalogStick.qml +++ b/tests/controllers/qml/controls/AnalogStick.qml @@ -8,6 +8,8 @@ Item { property int size: 64 width: size height: size + property bool invertY: false + property int halfSize: size / 2 property var controlIds: [ 0, 0 ] @@ -18,6 +20,9 @@ Item { Controllers.getValue(controlIds[0]), Controllers.getValue(controlIds[1]) ); + if (root.invertY) { + value.y = value.y * -1.0 + } canvas.requestPaint(); } diff --git a/tests/controllers/qml/hydra/HydraButtons.qml b/tests/controllers/qml/hydra/HydraButtons.qml new file mode 100644 index 0000000000..6c3070d2b1 --- /dev/null +++ b/tests/controllers/qml/hydra/HydraButtons.qml @@ -0,0 +1,18 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + width: 72 * scale + height: 48 * scale + property var device + property real scale: 1.0 + property bool leftStick: true + +} + + diff --git a/tests/controllers/qml/hydra/HydraStick.qml b/tests/controllers/qml/hydra/HydraStick.qml new file mode 100644 index 0000000000..3c22789f6d --- /dev/null +++ b/tests/controllers/qml/hydra/HydraStick.qml @@ -0,0 +1,91 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "./../controls" + +Item { + id: root + property var device + property real scale: 1.0 + property bool leftStick: true + width: parent.width / 2; height: parent.height + x: leftStick ? 0 : parent.width / 2 + + Text { + x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2 + text: root.leftStick ? "L" : "R" + color: 'green' + } + + // Analog Stick + AnalogStick { + size: 64 * root.scale + x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; z: 100 + invertY: true + controlIds: [ + root.leftStick ? root.device.LX : root.device.RX, + root.leftStick ? root.device.LY : root.device.RY + ] + } + + // Stick press + ToggleButton { + controlId: root.leftStick ? root.device.LS : root.device.RS + width: 16 * root.scale; height: 16 * root.scale + x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; + color: 'yellow' + } + + // Trigger + AnalogButton { + controlId: root.leftStick ? root.device.LT : root.device.RT + width: 8 * root.scale ; height: 64 * root.scale + y: 24 * root.scale + x: root.leftStick ? (48 * root.scale) : root.width - (48 * root.scale) - width / 2 + } + + // Bumper + ToggleButton { + controlId: root.leftStick ? root.device.LB : root.device.RB + height: 16 * root.scale; width: 32 * root.scale + x: 128 * root.scale - width / 2; y: 24 * root.scale + color: 'red' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L0 : root.device.R0 + height: 16 * root.scale; width: 4 * root.scale + x: 128 * root.scale - width / 2; y: 109 * root.scale + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L1 : root.device.R1 + width: 16 * root.scale; height: 16 * root.scale + x: 103 * root.scale - width / 2; y: 100 * root.scale - height / 2 + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L2 : root.device.R2 + width: 16 * root.scale; height: 16 * root.scale + x: 148 * root.scale - width / 2; y: 100 * root.scale - height / 2 + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L3 : root.device.R3 + width: 16 * root.scale; height: 16 * root.scale + x: 97 * root.scale - width / 2; y: 76 * root.scale - height / 2 + color: 'yellow' + } + + ToggleButton { + controlId: root.leftStick ? root.device.L4 : root.device.R4 + width: 16 * root.scale; height: 16 * root.scale + x: 155 * root.scale - width / 2; y: 76 * root.scale - height / 2 + color: 'yellow' + } +} diff --git a/tests/controllers/qml/hydra/hydra.png b/tests/controllers/qml/hydra/hydra.png new file mode 100644 index 0000000000000000000000000000000000000000..a7549ab2312328ad5c1ca2636b1c2b1efb840795 GIT binary patch literal 21273 zcma%jcQjmI)V2^M2!iM}K@6hz8l%oci6P495j}|BNz~}VAlm3HGWuwvL{CVJ-kE68 zdyD=qk>9tzKi)syyOvqAmOJ;JviGz1e$I1F#0w3T`-F6aSXfy1L8^)nEUY`fu(0mf z5MX0rVPU;&^~A!$y7yMqz!eLNh~nn&4pv$Q4Hgy_z8gqg2_Hj9_VB@d?09zw78VN@ zNKszLYig?*l=#}fowxBswz+P8@U>g&%cRs%>zBqNQ!!p@W80*JM%hzw>B~svfWM`9ZDT7rI(foqp#^{W5PX)rqAyupZ7AZ)cbn9 zrk?+Zv9`5kV%qM}{BwFNSvrkL=%%@Yg@u(N&FP1Wg@wgGaMOBX;r~9caPDLM`}q0{ z=f4~N_q+by@PD{V&TD{0#e!JVZ3N1E_BG&~Cy(V+r2EspJQkLp>`^=oov%|!GDL7? z)%kts@xjGerCAkM!5>;5BUxFjcWC~BuWH-RQhSeuI&}(5)IEJ@De9Uuq6I`-iF)o} z?b71W(Zork3t))^DVC|QA!ml^LC!#QE&5eCet-!$)iQNa++vax>z5F*F#pfBZwDFo z#K!PHLLlAhv0qysW##@dB6BW)dBag<&sDx17!k;GzrJ{>_=Di)&F{8eP0ZbCz=3>Y zi!~m98T&Z;!J#2ynN9P3i=UN5D};nvA%U%Br#en3qMk(FH@HDJf6_F=w9m(U+Sp0{mR=S|&Z8-%QL-Yc0q0VgQT zA53}k`8Eo^a@x{bSC_&DN?;3>^vaklz09ZlmyFaVV%z?Q`n=DTOM%Y41Ze7(Em=5Uvv9By51EAE=B*Dwu`&20F^apk8pDxUKGp5@2`PR`}v#qvg zIKPC54{d*53e<5P=1E<&UL0JX-L%H^`fS{s2GSn;VMAPlQpq6s!L<#o7hEL_uf=EIk3ZtCEl)?)zL|`F=9XZoBOZ zdDYGw`GxQZiE`fz8O}Bg9UCjs1P9 zdPZIiqd?fIO?1TK1dT%JhyM2grLcWzG>s4qaS+R&O>4Px>cBnf@}_<8dk50m@056S zet22t*_sUE#BK~LmnWEORO>lv+{E{3>>?*Y!z8@=&C;t38D9^3`_kzxh_vd}2WC7E zQi{M8MIYPulZl143_XW~WgB(~fOr8=f-zP05kZjLd9~d6P)bWpRaLtL3i$Z}9v#-r zOS1Y{L&!2OF9WYu0e66z!FqO#cDb-Mkxa^qnv8wD0=J^cq=H(@DHI5%YaroMbR9?NkO`s>7vY%lc}NyTPpe z4oh_^)5-az2dWu`>a(Q%JS0pP11$R`6^Nfu!fDcR}Bv5Hckosx#|LIeIm_^}}m34X)uA z46@xj9gn6nxkXPffE>c^kw0N-YlqT%0}$fatYr%$zrRog8NAJk-U0^Jx^ z`ZJ|4m)oAs^0)qR_-@Et?FC@@q~)afWX?D4SX+xWXEw=;N`x3CONR0h3Mm-{pCKn)9;$aV>~=<)QzLAlWC=B z*k=j~AJ4)+j205=%q!j|3_X_trUWuH#NQqg2C+mw+J3v>%A!$Z3qCV1Z$74T8Mqxz zu9i?O4EuAAfTL2=*~!WI5qhb|8F3cMXnBJJblH->IJ85X4yz4Vm!>BZn3up|EA~d z)JHJQo~hhvAm)4v1z)7fATTt4_nF%k4pN!?jI4dkFS^ZLPebtsQN!fuRDh0I)VI%E zDOpryt=ZsW6}CPJc>yezM#p>$9r3o}(pv11J6k(G7S%i0(#HOa_CD}p={LtZV48!Y z@<8JsWAt(b5^l*WMFeY8{w2p@P@k{QKL|x!V6%qAOU5oXH#hJ2tw_yi_6BYIvq>3E zvj75CM@-A*mQ!H*^NH}>7Xs9aXUBaKs5Mq|d7o|)B>)y>frpOI{g-;t0#mQO;eqGV z7>+?4fwE|vcj%jN;7685?g9L8eSyB7nVb8~b*#U=A9tJ?n1HY4Nc5Ds;)1RpPcY~E zy#?%(wsCt4mt)zmQz?x1_2r3a;HA4{kPHWj9>ZS*Ucr-|h0q+7U!RpXp=^`Q;&+Zc z2qXYvV*cK+OOmORuJA`yQzc?oHVC9ZN3*`LA}{(tJht@+;pE(|_3R_b?L)?Qysj@# z0{0WMyx@9N%}qk4Qs`6a?7Hb8arxiv!(HN#% zt-Wjiy12$HY3ca|@j_vqYo}ttvg6D0rCCOG-$4eYS0dYK8)<-9E0zJtF0EhKd$`=i zVUe8wPV~5t7&u>`4b!t934rdNf3sg_G#HRiC4!$@A`^y+7zu@DX zNQirVg>Mql%BGq^J^QV3%4>3O?x#d+`(x8UK%sO!{4*z{mkks`CQtVEZ5kWMpsA1n zN;MWX);PR&v8o~mu@g5}Wx>BM8;s^3I5>RDiEM4XMmYILb<$XuNWXpGe$d2E?ENOL zK$M?@R&Vv?tM%bG>gWU2z6{bni}h-rsKrl(EY$txZ!R;R$*QImVy>@FhK2c}C$MfU zpClW3)x{UE*M`qbmh*&t_KhvJVY@VO2R|_gFZwgL$9pCHWonTiS2F3!AQ`%}K~RwZ z4RP^^!koHylTzxy%vZ(6*qxU1?Ido!O*1O2zmt9X4z`cE+QM|3?0`d$AuZH7r?eW&co#{=68q9 zZxa}wSh600lI5W<_yReYVH2zwFHPcO>2i~D?Zz0(hpA(sCsQ)VOJVh88|MN1*c%Vy z1IkvsN9|rlH_8|3SZz|~XVy{xFaGq%#V+cDBN&8lZHZ#eYcZx&uROWvx!T?f{rOO) zBHMLw4J35=MJ!w`AtZ9dV{nE9?|WDF8^*Zc=fki?sRGI*UBaFR6gophxk5{B^=0RC zzB@PBEVhKrF0CJ&x!$arY$2uf!ZNL}W#_!bsZL+RN2bS9VUl0vJ{}I{B>m@aGyd#; zR`B$oG*M(dhO~n}cCpRu*+$65S|S(QagIKvJ^jn^+4nZL@sh^^lt}%g^Bd$j5VLuiN2SL-hZ>DH1)XI#PUxON(-Fsg4L+qWM zb6&=9C4swQcfOsgCWHW$xLyy(T@ZIUFGY{oA;AUYJC!X{#@OG%J(^lW&PDN z;qJ}t?J)BR8{yBRc6FGIBOiIP7a#Ogb0Y@7h?a$EhkVO<6mzk5M&CPa*B;>FF!%Zxaao8YuV7L?44)h0!(#1UymFjH z=*C&c^6}E~jhIlYW|Jp%L3VtUPo|>YbIl$)|JdY=3?b9O(E@8JJ8?p`C6~Ek_}h6$ z_;Xa~gdl?F@O6qL4TCG0Wog(F!%+y zz_ziud3JkqeB%g;sI91AQ9i?~#o{H^v$9grqEymC=-Ss!dv4FL255g>Z7h31g8xaI zJPNm{gC?=CWTVkG?P`I#_0P!(`WK6q z2C;lt{Pak?Ku3;=7%zBs*0{$xh^1JE%bX@b6#E?aut8^vV_mOww9Sl#CxVtx!@xGW z=<%Ke#Q4TTdFD$MM22?dvg5;kWo{ zv1{>Iz@qXXa{+EcKviAc6J>1NJpKXJDJ89?#+I>H{HpsiY~SA*F-^<$9?ZBJ`6`U> z>b1iZbP9dpD-cNtv0m45Uy9RQQ$s_;y^Vj}66RXU?Zs#IdwY52o-mb59+H_P<<`#; zZtV6NbK>2dw&L9|XoOe!5bqNVPEFRD$VN+ySGEDsG9*xsQKux{=Oje97rIWSgjBgyBUy8>{5Ln)Emo40SwY2!vkW~ME=GBrI z#*f0|z8_xJynI%tS3IZEU>0Ouul4Mq6fQ#>sE0klF0X`cTeXcoSA2>bNCSZ7EePlM zYxk~ycVkeYRq9+=3AdGJ4G{$ruFW9gUmJ{V92K9j)_{hEpN9eK6T%z_lkC|;w0d(w zEZ|lUKqk8~sb&cTfhQktX^_C?oi@l^fK2WsUNE6l0;OG_{lx6+?%a<3}dt+l;(r6iU z%%V6!(9M_g*`0to6be;Q@l`^e#P$|_0kIUFJAEm9+Df6bcN3m1O{p^Ubdr-VLk$cX z-p&K7CntbYo0n&4@fotH0q@*Oy$l}-^^EsFA0OEeXIM1HI@~^+ASBy4! z5xi){Seq-maK33X+J3nJqFveyG5@A5j+%8W8U`igLlIYl%$I`{)@VzEc0gA^>vbDG zGi;R6yzn;h{3G$xJ;{E&Y-@YFD(+dY;L@uB+`_R(@MPtg+}Z&<}Kb=pEmOGtWAtl6k z!P02Ci`|ZWEH;{(=n?i__BT!ZC3eBcXsI4*me(0pgTxD#Pb-`mIOydrH)?cSYY!!| zM#GJv(1mnSz0!!8pitxdfp!|en>rw;?aJC4@mSc zHfZOc+3^t1t+DLzN`cY1@5ePaA7F`59X?X&aQBye8330ZKVZO|NM**tlvh0Q{kxsj zURi8;NX0H9kw{_n^I%7W$@c?4Z*Kztd(4N$mKBMe*6V<@!jY%;eZyZPZee>d8sFQ( zCfQQVX)ga!yo!}nC3A; zb+;H9)m{tTe^4)si8}ANj|&C?q%x1Mu9&ud8$<10mJW_>i8?gi4<;P_u!`O}^4ao0 z#gm0_-=OysUR59BYWGeAPhV|qtyc=y!}=0LxMaL4Nth@i;9{>w%xR9TD7c0}2JO4k ze4_8oLV)edy)^+i5gO_$gHz9?7uv#Cep)4xe;A=-4^gewS7i*#*=W?FRJ9E_NEa(N zz5XQl&Y7XdIit`vp}qDHe5Y|oKgJqc}cyg}5x>VFaMXj1F4%;*zd9!HSx*Kwn z&6GRjErWH=Alr7B`JXXEC;yARo+NNVyiekSqu3BE;$--q&IHR>zCv>nnbohqjXz1>LfTbLCM;nIo9E%%HF1r+K^xI@d9nfaBKxdD zfH(7(l!+~mMR5*)s-cZn5(k1gJ2Ar+QHzIxt(aE7(<;dNP{H$2J9j6ioyE8^F?z&- zrtufuby%0HaVsV+QFzM@Rp&Un?tvOnQS!Y%!I}n%!oLlkAA>Ai*S6I|3KLF>^X3ZZ z#FEsN4YiDYobm?s4Gp6f|BP)JMny%L)41HpL|rPC6bst^n94~-RsA8dq{-tH>~1>f zmx^69s(;&d(4_A%lE3W3 zSe9-omec(C%IV^E2`L0p7)Y0s-+LfU+X)6d4WVpzpM@5rSsJ8u0Y6AT&*zVYI1dFL zAWXNmw;jJY#WxXo`jC+Nj@R)E-{l06m%w=TCP7VzLdV{U{pUQ z7iuq%%j=X!t_x_2A4%`b0SKfC1Fah%y{^Uq~Bz*qpDD4 z^kMVI$H)EsBrmsO1dvb%fB($6c7ReWJ~b6pXWPd-{(nr@4^~#WGTqV@bXMR>rl%if zCF&=5X^C%hYwPITAA)Oi2;ywD;!S}|_IQk;*#1`28q;*z-3s`d7yoc9wbsH)8E!-}|hPqU&ZALvokkl&Z)bhhut?I4gM zbFF!-5C7z1_8ohR2>Wo0(03m-yFFz7JY}mev%flPF)ba{cdTqmwpn;^%5oWS9q|O7 zP3Xm{rW}d$m-C6c?~xZL@H^ESUNUM2wYQBnbKG+f(u@aza(5Hama^td~`HOws)}UH>{qNcKxl^ zY=VxNo1R8_Y)y@g>9%4{uub)c_$}RO1SZcprL{R4VM9xj>g2471<+BbZ!BuS=B#B0 zH=ukR<-FLvHC?BttEl7Wx(jo(%j_SoG$o29u8o+NbYit+SE?~l;D9<@3L99 zt5UM=Ze%jLS2?ZJfPoW^!EzqEa2G6yb3|I-)GZ-ohhN=i^-TSf2<3(OVXqWSG^AZ# z+1f@-zDaR5+rNd>PZB>jbB;mj5L_!?_zG9}p>+}@mf_fp160-8npLsxA@78(Y7yYmOm4ZQO=unRMG0p`nK>oKcc$6-bQ<}>bL;5)%C>P*a{Ftn^ zCK_CrE$?yR1vAsLf#+Lwp)YQt;I12z=8-S&e_8;# zW0=_&Wb>bc2D^9ZFz^mLAHL%1o7(j4CPw(t2c$nPX@ zC?UX{hHGsgmgnp_z@0NB`&D0ecUXl7^8~5c&Of)Ay)W?jnyVn}fWyT>CCaFm2Gspc zZNsq@L)ejG;#X->?kgW(^nIAR|9XMC_1EdKGlP?Z!)%jx;@YE->??J4HuVa*0bjNd z$%)fI&#OwB$U^PawC;!kfdHBj~y>rJwQ)p;tP#W>;gxj{|dodgbLlIO;TAr;ILhvI4X>=kmVR-(g zTDiW&Tt6+Ke2hmHS0Yq`*}*hkXrZ?K!x8++@4m&M8UOAj zf9#^^_1XQNy&x9BZW|%~ZXK?N3(j&(FV49bldIgfJkouh12pd5zB+_FtpQb0@uGB8 za_dqMZPTYsb~o6IC4}aN7vf^|!;Ayti9aohH=C405!>6_CanSf<;~-3ZpDV*wo9og zBDpr-!hpa1gG7-UmW%Zf^Jnh}8fqx|AK3 z6t!4CzA@Y4f8L)dHR~wWCx|%#u)U6;_5HM$Gbym|xhinV_|qzJUAnZiRM_JKP>xk- zdDW=Ljc(R4rF+E5$!W#g1feV8tEveJ53HUHvBmit%AW{ZTVqcBG37d3qB#!zUCvES zO&OMx2*Wy;%pPY3FL?8Ol1@imZLKQSW`Cws&ElBH+h_qWSfF98XrSWzD`R1!hVczV zSs4jlu>4G7yR9hHd1&Y499+Wld`K9f!vzx5VYFc92e3a#Bix&wo)&IzFe&eNc1z?^ zfLC44C7;t#71T!OsLg~DQJeUz6+n?zH%pOC_mX-SmMCc~rnWrN3SoEi^Bw@Uj(-4J zH034QPW%(hXJ%)A9{5>UJdS+iB5XI>y|C~KzS6DQvv+)a4DeO8JRz04i1#@M^<_L8 zURe+~fkJa9DQ;^qc<_aB^(0St2+yX=N zZk`&gd9l|oagOoa8fd3@*So=kw7SwlTc3m9KSxJLmzS5P>$d+=rL!yq>qrh#$7*CnO^u>&jnVj)o8)Cc7WR*>t}cFJ^QnL4SGZ1ChQGYoyOmK>FzP;A zs#iX?aim*X02B0fj0UOTQrI=L`kn4yT?HT^#`^l%8FZtN1@Xe#eCcugN$KCPH!l!j z86-W<$0sM%Jk!#smX?-)mHtd&^$vcu;il;=u7b~_>u23yN~IJ&(-!vwA0)IO=Ns(Y z)i~vD(At-FenoA@zc`L==bOX!A-0urq%<}%V$;>SYaRUnOb38%yqGtH=){oAPkB@uA$~+|*E!6BQ4a&!3l7aMk672-+ui;iL8xjyp2&V+YIY-E< zhg5J|HWX1-mN5t&{v^IE*-f)n%+r_eCXFtT&KBra9gM@@X}-SteKv_iiumkQqh-?z z*PH^+r#(>3&CSiIx^YKO=<}$ksHlz{hxR{?Y$3gZ>grdAJxqP$S6&m(Zz??e%Xg3D z=Z7c;_BOM^mW?B^t_YH#ZsDapRBnGV4%Wfu}e;oxzyy0GwQT&8&WLlX{b2uuS` zs*)u_7h{J^V~gNlUmq=1?qvh1US*V^pXM$Z@aK3JC2lk}o5YPa)r`rLDQa~}wRxc( zy&uq7ggn^tNU#0MRp5v5G*0=v3#EaI!q|0s5sD(LonvEE>dqX%>MG)!ERuh~_1*Ha zrN+GTsqZ)7K5d8ve{;TPS*i)m+FU9b3y@Z9X8`%W`(&zJ}iD>%C`lNEIsIg zfQ7hrD5Ya_^2;|=^M<7RVvePq1|nhv|EBBX) z-rQoBdA4TRhJ0K~ax&+4B~3d-C{r)>Dhe747f@g{%w;(xQuqzZ$>&i^s=McKlAEBN zjVuHUqwXcvexG8(XN=+aF>GZpwZLAZ#Y=QVqN1_6y$y*<*l5hhDS@?%0&kYI9!tk6 zSP-FWpij3)tu)jXLRH;asqdr>~l zodjm+nIh~7o9TT;{`F^7v}DMKJ+^b;aBSw_VfOwQ7F{gh%dFAJkIpD-ewqR62anfa zRXpi#n0(#9u_uq?I3ja}HKf^%$BW`tPtKc7%4Z#?*MO{w1(2Lr_S=kP1xKFjuKZUM zoE_aEsPE$=Ln;Y27n`RcFGlY8JmWTEuo|^x^KxD zu6kbLH{N$(^uZiah}qfM*{RNx?_xN6UKvC7H`~!{M74>TBis?XrK5JR-QE}~aN5TY z(OL}x%3!6)XsEqK2yBRzgZ^iKNOI8Up<0xDS>%ehAlf~7_^q06qhiM4WJLWmk0lHi z*1PO6Xao+Q)YN|Z%~cC*DM<~qVSEl)6xU4X{4}4|cQE7bFImbNxqc*>_D|-eXuZ98 zL6=0=`|>wykqQombKJ_44g@&Rid2`R9A0Z*$nll+Cwwq$q60hU?n$r^iazFNrPBNb zCb8VxB$Pt&`-Rvc*Ux1)>25PjBhl$4mM>*xQ}<9Pdh3{MXmxhZx5q9o4&p0AsgMF- zO65C~N2$Q?p=3P*A_Y{1b?VTN<36uW!BZYZoAd=s_Drkl@4c^Ya6ijWdd!&NVsU?8 zP|SszF5Ut9}xa?Kt#vlawwj9b@0|?LZnCcIB#+QADc$)cf6uNa6ED1l$fS|ad_Wn1l4gsPqM zQ{rYmwH6yBl;8T7&>v|PFSV|OO$QC&hpsLqy9Iza$;GG32ZU`k{ADg-#%JI@o~)#S zJZE_wGW0>l^3YDeS=-}@9<_wd6MxLf_Z=1ocetwJ2BL!Rt0N;jP!mjaE35Jk67=*l z-Z*NUbwVF5V~hC`)};25;yjI}b?{yGcBI54DF9tPqqg4CM%Uue^rH-lUK3h>Gm6Xo zV7GTDFHaUfL%qC7xMyo_D-o+8Y5lJTm%-m-2^O|4fW2Z9Pdhl^L1Jn1eW-})!h147jYP-R6t?g29uV84L49AVC;n8SF-t}9 zdGZNFHPvGz4~g78rYN7Mzbo>)_c!=I%TgS%6TzG$vONeF!P|=)c?KROJ|@yfMGe0W zeVwu5jnK9C^)|lW&{GRR9-18L__3x;7-(I}2>x8=G#mt(zX{|L)z4 zYWw}6x6>(sr2p{0ekF$q|hbmqQb_Zu9S5hQ1o^3|HE!gn0>fSp+XAh-mkEmYjzSiZ&_!VzX z@}R#3&&B(&^hxB0O1DUXt@(&9M%g@SgvX>i z2Zh)i!dEv=g!q_{^*AG;)KlW{=N2EqUZB3vHlQUt6n8sg{sGAS^j{e_qN@ zLX{F65S&(Ft2Vb`2`-u08#?=6@XN7O z8ePJkjHLY-{7<-q16qyz_+qSixwf~RU`gbK7K&v-$)QkqEqp;Xc^DbT%JGKx6T4f% zeZ9jne&Wf3_T}3jdbjuX5++u~<8tE%SG#5k+UraeI={^b6Y3W4f5G$d_1Deg!ashT_K{~BX%P7=v(zeij8Rcj4EI3M6%AWRFRRQdhNcsSoVS+Rh@*}!~zBc z>OGvDaNO2z82@*is(bfu$Mb@fSiv#9hzmbOk%Rqr;Q$o2WE>*G`ytiXQJk^4JJoF| z2*<@nDU@3X33Y(j+`Ii%gfgX)hm`|QqScSdhg0dF6g=uxau#NPLMcK}y-}~hnbu$u zEr6209{2$ z4TWCbV1@hp+aJS8JJv>I0Jx`o@ zi)gdn^^SeV8;iq)PntzNrUS364(e7m(FXRnmrb@$6Zsc{zgM!FvvMsVjJm&p)`e;e z+uV)eixEUOi*UQyE4x7o;+B*qvJ3TM;wqafR@#x5AHH}FoxTpi2j zuE2=C=KLya4h^?jha8?f)?XC{|9~qoowKN@pH#xx_{Rz9s6c_Wx4lX%0l#OZaE3jcSr?|gga7ma)h@7?^nf#e{Bse2U9s>K$Xigw5$jL2;SYfb;9yDRC(Q3;jC~e_WNRsYccL(Rw-vs z)q382$IEJ4X~1$B82j1lp2{2w>OOfk7GAU4OaHp4I#GjE{dU1YraZf}zhIt1>6JiP z{438erghnjV(^38YJs$SkrSrP5!3JY`U3vw6@Ns_G8fsK|FmhF8MC0b0^L=$&wAF{ ze&r`~7}?*p6ukp10eprIOYE67ydo`-t09x?Bhc{r>zXO%WL`hqmRht*(|)+lZhfwd z-+%U1oal(OMLEVKPvw0&4*`jqq?G|w-rjiK>E$7C3|){6;e{6cVafMC#`S)gBt>GXoc*_0T=_M zlf_7DWo2t^-5??vfj10J4~ zPF1~DGcBbT7UG}=s?HNU4jBR3ScC|WG=EZP4-(|Nov4Eqe+IxGpVv<;hJBK)USeTo zeOwTKsnHV*#Ub@jiiz2@S`ii{yN$d}?Q=_#N@^P)PpOC5iF#=}6tv(>cW-bO!f>$S ztO{70*TN~oZ%`VtU_kH>x&4Nxk`pB6ez^g^FP8-hTZB_Ue18f3iKgr+*%Ocb zr9E?P^3FxtYn7JiXz|xyEgtbjYl%rvfz0+_ETNP~`D=XLt2&Fos1s=br&r!F!i~PU^V5IudrR#J%kL*7J4)=b! zJy{)Sn$|BlvPc!gzYP%r)qY;cfk) zkK}y#I+_eh;2>w_ZD^r3FKnZ6u-hBMlwy3(!iBYFES2mL+O@~IibosV1_?(0I`F## zWw0RdG$L;l()d_Gt$9kT1wNRsx|u=g)#0y;zGVf{&$fa-g>nKp8`NhJ6;p0YY*u!W1+RsTk3I1yW8Q1 zsnhnVU$})MCrl`0WRY`wjho>Zlqe_WdlES`dJ@4F zn(fhlUJ@NQSEhVT()2Duzqp-wDo=7Y#BRsfv6ZN_GSH;#aw5Ls zi*~T=U+HZhipT56z|Dd&LgZc*jRofw{j?(}0XoJV-t*}fbuj=A%z2^%tuX!SlvRc9 z4S?fX=ELACd)-e1m~L5!17{2ec0*a;p}(+3k5)J{ro+6!i4yP+i=G3Ak4S=kX;Crv zx-^H6mG^BptSf(cWk_Ble%q@4uWfpiN}K+auPVdq;tQo%66eHa_<0y8{BMGRblfwN zGpro$q(~Y)n3u4?T7Dd=&fEETx`RJt@!x>aBcU;~F)CCs8j~9$9#7Tj6y-7p;#~Z1 zOTWu^3PJ!&gzjSQZe9rqGqgF?dH*-1cr3-;J9z){em{yd-U~L`j&MCfW<%E01;-yP z2a)}gR4I3En^@Ko`q5RZbkvIuS5L3^bsF$&tw|0B@jw;N)|kq2Wa8Za^obIYLh`C{tTBn864_(@Bfowqk81^GON5Sb5i#m zuJ2Uk$5*72-(v#h{%~e6?7`BzNyN;vSI9^0jwbSoK%r?MO(r@TDj;y;Umy@7duoTI zlVI#I@uo4f%lW_pW7f(l9R2R zg_)Y81Rlq2dA$IaAWe*nq^HvPBvz%^PqPpf5}bcxqLOB&8bEpbVa1hKe&^dGAx`kacIS_Vx&o8bzGA#3D$ zWl#Ef06N`J0U80U&TAMv9crI=45NRpRFa^|Hj~FDaFt931`=Gh?TRk`WORm2FUYm^}1*UpbO0n7HshV_x6W%BMdzgs6V~ zX~lEgTT_wbH`3QPJH-~rX%2)uU(=d=<5Zcs-0YDzq<^nxehTs-p(fo zcO(>{!v)tbuKPNAj<1hCi-|@uIPrw6ES*qi_yNG3Iq?r=G)tW$(C%7gi0y{ zkHfCdbI$XzDxU3uM0{?v8f=!Sz}5d(+Ed~0`Ul$De&4>61`(2|8D5ny*zM(E*a(B6 z5rZ}rRm8Oq{-q5rX=Qbg&nD#uNIo(8+3M^cI63D$&AG>MNnp!vv47mpkOI)+$Jv<= zU|R_MP-Q=!;VI%h?V8~SfxfNc1^z8;)HA16|2I-xTT#fA+{~!kT-&D~TLHj+vZt0x zwns4H-m^{lDPY1SL?5AR{_&PF7p|ud5wh;5B=RrSLvyRtV~@y%``~lBjV7n`2QwAzMV6JoR;gc!XoOucU)EhtgXy0H7p&qNepTxj$gesS8wb+eSHh}w4cB85tWuH z^LJt|Qpq=x8o0ZY+(HU?s%vU=Cfwec{0kg?#RDxv+}zyS%1S>b+z&tGb?w)dB9X{_ zpLhRUT*9K7QTQIKsF+w0MIG74pY_xms_LqiMI8ZE@wMDL+3ay0j;gN$3IE&OwYx!M zy!rE|I{8m7>z^d<&tioIif7U!`=3KrT!`2;G#WQHHh#MFM_lS36EG7J;#P3l&iC}`%hFLm<+K|fv-`oQmZ`KZ zD!ZbCiy$Z_CZ_5+?h_)cC}J!u^6Z;)0dDvFu0#(Uv+IG8P=r;rgbcghTe4n8xs$>g zsVJ0yrLps?{)48jE~R5Zrgh>Mw}8R?N>x=oZ_|h8?`4D7G8Ar(ex%sgeBfkjw@(?H zj01A67}jRcY0qr`g)O4u)LYPWuE%ym~SuBq@^0KC34CH zb9mzasv+6sS_rdBphX#KuCFTIUu8*jJ9&w*qoXeu%6>M01fePqFVh5E7f$tVM&m~Oxm{ouv+dKV9g z9ssTc`l^$M!Sx2XMftbVyBHeOm4qh zTdAH`EBB~C+H;$sQRL=qhBILcZ|_u#(n`bUJEoLYZ@`Y=zCnG>kS5~KP4)ZTY@_nd73}u@tZvRLraCRHDPhV}$j$w`+><6}0pLaa~ju z8A7Hq$e;k08}{PNT213;4sg}L#&}ocn`eb3;p(u2LQho#(1z(E4;H!T?g=ZvExcaOVP4_M^Lt!T5oS0C66zo&7JOIQzD@t(-QzB&D&h(S~4h!X8chEM;l z_d60~3=aR?$Z&=&N!R1QTqlGW7FNQ(-8wH|Hs(CipI_Ovu@-sv zRZyb@Q6+al6;Garo{=op4GTAliG`GwmOg*}JmYivlBwFO^$Ev;s-eaFo@GTi%@YTO zHR&m~LfF*A1ScAX^S8YasN}ub%wLucJbJX(prh}v^-|e(AJ)nRuR-JwPy)cv&2>-9jLbC(b5XF z6;UH<)gCP+MoWztNrlvWh)um$qVM&7eD4qUr*qxszJB*P=YRg^zR!;W-u(brG5x6w zuGl6J2t7T_b?516oC+tQsJ^YEkwPP5KZrALo%@8n=Y4^!YS2D!yMybQ^Sp@<_D6e} zpK?@e_w~UFE!wfPzgbJp3MFi+b=mFSK$Jzx`o&4j2mWh4KVZqLd&WAf=A*3w*29sr%?yRFWfg*YH&#jJlS+K?6Rpf-Q^HW8N7lW zyMASBZ59uT{0h#2y2okaf`u24_P2HvL;*B|m%5`kN9azr&1k@Akn5a3w`;YtuR!vO2kxvz7rJtO%B2lihwX8(!@;*;+PQN>mZmQkT+?>MBLbaafOwxvkos|hB z5@|RI*=_aY3{mF{xwzCdn607D{#+vuKvO^9%MbPS^MlCC3%N_*w|O$?3%QbBfU*OL zAzwcBEy|({*ZE>BlF;C$3(c!C{ncLR@JDu~T?QBq0F6oEU^V3jS#_`>c(LSi`chB{ z%VlDvxV#jr&^qV%{dw<4WqgR;qyq~O5|_e?MiSvo`HM>@a4J%pr({n{wuxwN{G#lieH9+d=VIFUyQY;8C zAD|L45o^%oxiC5qi7ft!qH)Kx?N6{LbPncUwx&byQs}UbmduNN&t14d#!3g0{q47WWl0BJ zD%LHF%b|qUY;%hKC+$}dM?oQOe)0A&{$fQaTOWTC+2|cgN@+Q?tb@@STH?}8kCxnH5`2q9UblM{nTKk5n>@s zbHz!=yo9$+0rSvLPEt$>^e2+!UxOWoh9jiV&tJ3+_V=hw%7ZyNrV{=^DxBYMoZh`g zteqKnC!pksDK9_gE*Z{pSNS5Wd=r%$AA`|)SrVWaLn0UF)N#=(Y0#`^K}4`B;vrGh zROeWcCt^hI=Y$rgy^(LyPj^=jhY1yPnAYxe@X>2Nt^g()o{BtK{&N(fk^CoA5&2H6 zYY)KkYu$WbD&53cfik)4X)20gd2_P!Y^R>jd1jw3@Bj&PVvV0Olj_(JOGS0mBdqOmuAToii_fdjHT8>9!;ST1J(K&pytNL#`=1A zjj$S=w(Vn4QG_$Mjo zJ>+el-kW@bciK88O<49A-RAbKdrk z`BrMO2=a)8k2wKjK{`OK4NmCP9yvk6NA-ui%)?5snDJ$xypvoPx<2$YG!O{g$ymE* z*_;rqi!g%WgPd;ShpT}rer6;;S6A1N5Ns5^!suy~Ch8vEx;6i!udk0p@=O!&@Zpl7 z_zo_)eZ8_#Vb(aW`PbP|<1~-x=ug&VfsZ(kg=h-uAKv2?KF~9#bg;`8c=v5d{$P52 z7x{HBFn#9pSqq9sR?#bnqfD5V!Vq9DN_{TA`te9VBx>}e-0B`n1YthWMOh=90i^L4 z^a;>8>g8Zu)H#YS1GPfen>x1RQfm{l9vg<%ZuyZqvCw z2@yO6J8sr_U|hS z+II|f&3^TGY%&Bd*Izp(D=S-m$V4M!5*ZsG$GuhS%sxuzW$tO@y9+ht2FR9CHW_*Y zQ!a<2{r&tPmoCjGoiaBiE;j-p4!NtQyxbWG^}oF9rYIW&Ga7ARH8wu>JZAtkb+idxP z#bT3z@&r5D1_J;>wBc6ZVq`wA^`ibO9?Z>{O5Erhw)6M(Ew8AU|0DP?7*<-G6QiE$ zzP4O@n`yZx1bEKTQS0CQJ0;Vv9b;R}2|A8cS%lrS~aRi0dZ zGuW`QOsEQ5C2wIL$M%lp7a={GKQJeStu-w2^YhpHKY&fEXo)NKc-UqXd1n=Aj`tQ} zr&@Q6rr@`xr&lWVC9k1ZAOUnVDtU4KMd< zos|j!Mb=z2Fen2hoj#(k|3z&%wXi^=Zz_Zn8T z^;|*By1Cev=tE`G5nduCn0h_Aw7;0MD5ZN35T*T38!*sxcJj$J`IbGmnhEjZgsh~n zTERo0-M#x)#w~CDOy$1mM zFjKo?Hor*i69e=B8v+*;1I80_DYi8enuxV&A^+y{jO~E{mhzv|Xo)QPe6X;mI=HRR#h~TDp~oZrRD+5u8YpR3$TZ=$`{)1y}GALz1Yc+d2Iu?OY>96*zu`RU{?)_KOKQ>mU z|H=$dqtpQUHPlhXFfAD*=w&{Vw6ukr|OzJLY~Js(p(VCiA|{pj3HjDO4}es#Ve?a}ga0NhMZoiLsUo z^7LN5pNF5BnZe_!&56aMqTlK5xRDt}o6t0iG!MG_Z!dMF&|*#}Q9*f8d%M&$qzT{d zl8ZFQq?0mVb{M=vzB>W?q+75_l;-3xx_u^pC=4QBW_sgh_33=RNmW9-2uJ5D=NQ|R ztRto*8Ngmt#Qf2=^NCUmGCy9TehF=wFFg~zi{EjwXPZ|~9kw$ht7C@kK>7Mi{NO^t zXU03xv%!vT9>TqW!n1f-nN{)VztK9Z^mZJPk&&=cD$QH$;m6p+UuBh^wxmW#_QNnM zwrA4#hq$?|{1(M}XMC`xL5wbyypCOz${}~$sdLmh-m-&T9Wpir>l0nu zX)mHVC{c-tJKrT94X69J9`D#t$=;$CWNj*)3Zby9B5yRfFj3j9pbNyP%d00juIc*3 z%hba*!^1mGJRI%s>l;Pj$=Tb|IoZNRk*?N?C2M8N^=smYXM#?N_83@Hvx~ssFh0z2 zwg%$&@$AX*)a>ujCcM*~JF3AXW31hM2-pp+u}!@xe{dliYiP*yNgbeV;ZjB#1hY>gtn5vmsWXw^^Q zaeX3GKx+ry1s|O1y!GS1z$UZ;+8Er=7*kuAF0ylibraryPaths()) { - qDebug() << path; - } - - for (auto path : qApp->libraryPaths()) { - qDebug() << path; - } + new PluginContainerProxy(); + // Simulate our application idle loop QTimer timer; QObject::connect(&timer, &QTimer::timeout, [] { static float last = secTimestampNow(); @@ -122,11 +117,12 @@ int main(int argc, char** argv) { auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); } + inputPlugin->pluginUpdate(0, false); } - //new PluginContainerProxy(); auto rootContext = engine.rootContext(); rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); } + engine.load(getQmlDir() + "main.qml"); app.exec(); return 0; From 20416455dbb61b7c139ff094182ae144d42eb293 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Oct 2015 18:06:58 -0700 Subject: [PATCH 049/232] Adding the loadMapping feature --- examples/controllers/controllerMappings.js | 25 +++++++----- .../src/controllers/ScriptingInterface.cpp | 39 +++++++++++++++++-- .../src/controllers/ScriptingInterface.h | 2 + 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index c39ece4bc1..d314df3f4c 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -12,7 +12,7 @@ // Assumes you only have the default keyboard connected -myFirstMapping = function() { +/*myFirstMapping = function() { return { "name": "example", "channels": [ @@ -42,31 +42,36 @@ return { ] } } - +*/ mySecondMapping = function() { return { "name": "example2", "channels": [ - { "from": "Standard.LY", "to": "Actions.TRANSLATE_Z" }, - { "from": "Standard.LX", "to": "Actions.YAW" }, + { "from": "Standard.LY", "to": "Actions.TranslateZ" }, + { "from": "Standard.LX", "to": "Actions.Yaw" }, ] } } //Script.include('mapping-test0.json'); -var myFirstMappingJSON = myFirstMapping(); +/*var myFirstMappingJSON = myFirstMapping(); print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); -Controller.enableMapping("example"); -var myFirstMappingJSON = myFirstMapping(); -print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); +Controller.enableMapping("example3"); -var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); +var mySecondMappingJSON = mySecondMapping(); +print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); -Controller.enableMapping("example"); +var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); +mapping2.enable(); + +Controller.enableMapping("example2"); +*/ +var mapping3 = Controller.loadMapping("E:/Github/hifi/examples/controllers/example3.json"); +Controller.enableMapping("example3"); /* Object.keys(Controller.Standard).forEach(function (input) { diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index abb9544892..80816d9773 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -14,14 +14,18 @@ #include #include +#include #include #include +#include + #include "impl/MappingBuilderProxy.h" #include "Logging.h" #include "InputDevice.h" + namespace controller { class VirtualEndpoint : public Endpoint { @@ -176,7 +180,8 @@ namespace controller { QObject* ScriptingInterface::parseMapping(const QString& json) { QJsonObject obj; - QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8()); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error); // check validity of the document if (!doc.isNull()) { if (doc.isObject()) { @@ -194,12 +199,35 @@ namespace controller { qDebug() << "Mapping json Document is not an object" << endl; } } else { - qDebug() << "Invalid JSON...\n" << json << endl; + qDebug() << "Invalid JSON...\n"; + qDebug() << error.errorString(); + qDebug() << "JSON was:\n" << json << endl; + } return nullptr; } - + + QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { + auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl)); + if (request) { + QEventLoop eventLoop; + request->setCacheEnabled(false); + connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit); + request->send(); + if (request->getState() != ResourceRequest::Finished) { + eventLoop.exec(); + } + + if (request->getResult() == ResourceRequest::Success) { + return parseMapping(QString(request->getData())); + } else { + qDebug() << "Failed to load mapping url <" << jsonUrl << ">" << endl; + return nullptr; + } + } + } + Q_INVOKABLE QObject* newMapping(const QJsonObject& json); void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) { @@ -286,6 +314,11 @@ namespace controller { for (const auto& route : routes) { const auto& destination = route->_destination; + // THis could happen if the route destination failed to create + // FIXME: Maybe do not create the route if the destination failed and avoid this case ? + if (!destination) { + continue; + } if (writtenEndpoints.count(destination)) { continue; diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 36f62a3abe..64e7cb769a 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -73,6 +73,8 @@ namespace controller { enableMapping(mappingName, false); } Q_INVOKABLE QObject* parseMapping(const QString& json); + Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); + Q_INVOKABLE bool isPrimaryButtonPressed() const; Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; From f90f0ed1f342c37b2e8187981f10022a8021a425 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 12:34:32 -0700 Subject: [PATCH 050/232] apply Huffman's feedback to Sam's PR --- examples/controllers/controllerMappings.js | 2 +- .../controllers/src/controllers/ScriptingInterface.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index d314df3f4c..4de173f16c 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -70,7 +70,7 @@ mapping2.enable(); Controller.enableMapping("example2"); */ -var mapping3 = Controller.loadMapping("E:/Github/hifi/examples/controllers/example3.json"); +var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json")); Controller.enableMapping("example3"); /* diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 80816d9773..6edcaecd3c 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -209,6 +209,7 @@ namespace controller { } QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { + QObject* result = nullptr; auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl)); if (request) { QEventLoop eventLoop; @@ -220,12 +221,13 @@ namespace controller { } if (request->getResult() == ResourceRequest::Success) { - return parseMapping(QString(request->getData())); + result = parseMapping(QString(request->getData())); } else { qDebug() << "Failed to load mapping url <" << jsonUrl << ">" << endl; - return nullptr; } + request->deleteLater(); } + return result; } Q_INVOKABLE QObject* newMapping(const QJsonObject& json); From 293b7c12e151b49305b4d87137816aee387c4e1b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 12:46:05 -0700 Subject: [PATCH 051/232] change join to makeAxis, fix some warnings --- .../controllers/src/controllers/impl/MappingBuilderProxy.cpp | 3 ++- .../controllers/src/controllers/impl/MappingBuilderProxy.h | 2 +- tests/controllers/qml/content.qml | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index fe7a5c24af..c6031d45d2 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -39,7 +39,7 @@ QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { return new RouteBuilderProxy(_parent, _mapping, route); } -QObject* MappingBuilderProxy::join(const QJSValue& source1, const QJSValue& source2) { +QObject* MappingBuilderProxy::makeAxis(const QJSValue& source1, const QJSValue& source2) { auto source1Endpoint = _parent.endpointFor(source1); auto source2Endpoint = _parent.endpointFor(source2); return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); @@ -82,6 +82,7 @@ QObject* MappingBuilderProxy::from(const QJsonValue& json) { // Endpoint is defined as an object, we expect a js function then return nullptr; } + return nullptr; } QObject* MappingBuilderProxy::enable(bool enable) { diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 9531d8bbea..a29f7ade39 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -33,7 +33,7 @@ public: Q_INVOKABLE QObject* from(const QJSValue& source); Q_INVOKABLE QObject* from(const QScriptValue& source); - Q_INVOKABLE QObject* join(const QJSValue& source1, const QJSValue& source2); + Q_INVOKABLE QObject* makeAxis(const QJSValue& source1, const QJSValue& source2); Q_INVOKABLE QObject* enable(bool enable = true); Q_INVOKABLE QObject* disable() { return enable(false); } diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml index 4beda82df3..5d81ead2fd 100644 --- a/tests/controllers/qml/content.qml +++ b/tests/controllers/qml/content.qml @@ -83,8 +83,7 @@ Column { mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); // Constrainting a value to -1, 0, or 1, with a deadzone mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); - // change join to makeAxis - mapping.join(standard.LB, standard.RB).to(actions.Yaw); + mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); // mapping.modifier(keyboard.Ctrl).scale(2.0) From 251a55b1fb255355ec5f8fbb37b96f1f9c982018 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 12:52:37 -0700 Subject: [PATCH 052/232] rename hardware controllers to not have device ID in the name --- .../src/controllers/UserInputMapper.cpp | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 0d0ae7d85f..fbf1994d87 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -31,14 +31,28 @@ UserInputMapper::UserInputMapper() { UserInputMapper::~UserInputMapper() { } - -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ - proxy->_name += " (" + QString::number(deviceID) + ")"; - _registeredDevices[deviceID] = proxy; - qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; - return true; +int UserInputMapper::recordDeviceOfType(const QString& deviceName) { + if (!_deviceCounts.contains(deviceName)) { + _deviceCounts[deviceName] = 0; + } + _deviceCounts[deviceName] += 1; + return _deviceCounts[deviceName]; } +bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) { + int numberOfType = recordDeviceOfType(proxy->_name); + + if (numberOfType > 1) { + proxy->_name += QString::number(numberOfType); + } + + qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; + _registeredDevices[deviceID] = proxy; + return true; + +} + + bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { device->_name = "Standard"; // Just to make sure _registeredDevices[getStandardDeviceID()] = device; From 7d48fe918729f9cd6f5d2014a3cb7673b2a0becb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 13:00:43 -0700 Subject: [PATCH 053/232] don't include Standard device in Controllers.Hardware --- .../src/controllers/ScriptingInterface.cpp | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 6edcaecd3c..a51c587afd 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -481,31 +481,34 @@ namespace controller { auto devices = userInputMapper->getDevices(); QSet foundDevices; for (const auto& deviceMapping : devices) { - auto device = deviceMapping.second.get(); - auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); - qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; - foundDevices.insert(device->getName()); - if (_hardware.contains(deviceName)) { - continue; - } - - // Expose the IDs to JS - _hardware.insert(deviceName, createDeviceMap(device)); - - // Create the endpoints - for (const auto& inputMapping : device->getAvailabeInputs()) { - const auto& input = inputMapping.first; - // Ignore aliases - if (_endpoints.count(input)) { + auto deviceID = deviceMapping.first; + if (deviceID != userInputMapper->getStandardDeviceID()) { + auto device = deviceMapping.second.get(); + auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); + qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; + foundDevices.insert(device->getName()); + if (_hardware.contains(deviceName)) { continue; } - _endpoints[input] = std::make_shared([=] { - auto deviceProxy = userInputMapper->getDeviceProxy(input); - if (!deviceProxy) { - return 0.0f; + + // Expose the IDs to JS + _hardware.insert(deviceName, createDeviceMap(device)); + + // Create the endpoints + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + // Ignore aliases + if (_endpoints.count(input)) { + continue; } - return deviceProxy->getValue(input, 0); - }); + _endpoints[input] = std::make_shared([=] { + auto deviceProxy = userInputMapper->getDeviceProxy(input); + if (!deviceProxy) { + return 0.0f; + } + return deviceProxy->getValue(input, 0); + }); + } } } } From 7956d737ab1b50fecf2c72253ff7a612e4796975 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 14:08:21 -0700 Subject: [PATCH 054/232] fix thread safety issue on JS based fliters --- .../scripts/controllerScriptingExamples.js | 16 ++++--- .../controllers/src/controllers/Endpoint.cpp | 5 ++ .../controllers/src/controllers/Endpoint.h | 13 +++-- .../src/controllers/ScriptingInterface.cpp | 48 ++++++++++++------- .../src/controllers/ScriptingInterface.h | 20 ++++++++ 5 files changed, 75 insertions(+), 27 deletions(-) diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index b3dc92029d..93e91c6b00 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -23,17 +23,21 @@ function findAction(name) { } -var hydra = Controller.Hardware.Hydra2; +var hydra = Controller.Hardware.Hydra; if (hydra !== undefined) { print("-----------------------------------"); - var mapping = NewControllers.newMapping("Default"); + var mapping = Controller.newMapping("Test"); var standard = Controller.Standard; print("standard:" + standard); - mapping.from(hydra.LeftButton1).to(standard.A); - mapping.from(hydra.LeftButton2).to(standard.B); - mapping.from(hydra.LeftButton3).to(standard.X); - NewControllers.enableMapping("Default"); + mapping.from(hydra.L1).to(standard.A); + mapping.from(hydra.L2).to(standard.B); + mapping.from(hydra.L3).to(function (newValue, oldValue, source) { + print("hydra.L3 newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source); + }); + Controller.enableMapping("Test"); print("-----------------------------------"); +} else { + print("couldn't find hydra"); } Object.keys(Controller.Standard).forEach(function (input) { diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/Endpoint.cpp index 3f1d12b9de..5c5aa6cbc4 100644 --- a/libraries/controllers/src/controllers/Endpoint.cpp +++ b/libraries/controllers/src/controllers/Endpoint.cpp @@ -11,3 +11,8 @@ namespace controller { } + +// FIXME - do we want to include the source Endpoint::Pointer in our calls to JS? If +// so we need to marshall this across the invokeMethod() properly +//static int EndpointPointerMetaTypeId = qRegisterMetaType("controller::Endpoint::Pointer"); + diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 5c6e6c028b..f3cd163071 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -23,7 +23,8 @@ namespace controller { * Encapsulates a particular input / output, * i.e. Hydra.Button0, Standard.X, Action.Yaw */ - class Endpoint { + class Endpoint : public QObject { + Q_OBJECT; public: using Pointer = std::shared_ptr; using List = std::list; @@ -31,12 +32,12 @@ namespace controller { using ReadLambda = std::function; using WriteLambda = std::function; - Endpoint(const UserInputMapper::Input& id) : _id(id) {} + Endpoint(const UserInputMapper::Input& input) : _input(input) {} virtual float value() = 0; virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; - const UserInputMapper::Input& getId() { return _id; } + const UserInputMapper::Input& getInput() { return _input; } protected: - UserInputMapper::Input _id; + UserInputMapper::Input _input; }; class LambdaEndpoint : public Endpoint { @@ -53,4 +54,8 @@ namespace controller { }; } +// FIXME - do we want to include the source Endpoint::Pointer in our calls to JS? If +// so we need to marshall this across the invokeMethod() properly +//Q_DECLARE_METATYPE(controller::Endpoint::Pointer); + #endif diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index a51c587afd..68095505af 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -60,24 +61,37 @@ namespace controller { QJSValue _callable; }; - class ScriptEndpoint : public Endpoint { - public: - ScriptEndpoint(const QScriptValue& callable) - : Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) { + float ScriptEndpoint::value() { + if (QThread::currentThread() == thread()) { + updateValue(); + } + return _lastValue; + } + + void ScriptEndpoint::updateValue() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + return; } - virtual float value() { - float result = (float)_callable.call().toNumber(); - return result; - } + _lastValue = (float)_callable.call().toNumber(); + } - virtual void apply(float newValue, float oldValue, const Pointer& source) { - _callable.call(QScriptValue(), QScriptValueList({ QScriptValue(newValue) })); - } + void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + internalApply(newValue, oldValue, source->getInput().getID()); + } - private: - QScriptValue _callable; - }; + void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(float, newValue), + Q_ARG(float, oldValue), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); + } class CompositeEndpoint : public Endpoint, Endpoint::Pair { public: @@ -108,9 +122,9 @@ namespace controller { virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue += newValue; - if (!(_id == UserInputMapper::Input::INVALID_INPUT)) { + if (!(_input == UserInputMapper::Input::INVALID_INPUT)) { auto userInputMapper = DependencyManager::get(); - userInputMapper->deltaActionState(UserInputMapper::Action(_id.getChannel()), newValue); + userInputMapper->deltaActionState(UserInputMapper::Action(_input.getChannel()), newValue); } } @@ -327,7 +341,7 @@ namespace controller { } // Standard controller destinations can only be can only be used once. - if (userInputMapper->getStandardDeviceID() == destination->getId().getDevice()) { + if (userInputMapper->getStandardDeviceID() == destination->getInput().getDevice()) { writtenEndpoints.insert(destination); } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 64e7cb769a..07ddd625f5 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -138,6 +139,25 @@ namespace controller { MappingMap _mappingsByName; MappingStack _activeMappings; }; + + class ScriptEndpoint : public Endpoint { + Q_OBJECT; + public: + ScriptEndpoint(const QScriptValue& callable) + : Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) { + } + + virtual float value(); + virtual void apply(float newValue, float oldValue, const Pointer& source); + + protected: + Q_INVOKABLE void updateValue(); + Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); + private: + QScriptValue _callable; + float _lastValue = 0.0f; + }; + } From 8578b40236d9f4dfbd9b4feb0098133ce94a2ece Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 14:14:45 -0700 Subject: [PATCH 055/232] get JS source functions working across threads --- examples/example/scripts/controllerScriptingExamples.js | 3 +++ libraries/controllers/src/controllers/ScriptingInterface.cpp | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/example/scripts/controllerScriptingExamples.js b/examples/example/scripts/controllerScriptingExamples.js index 93e91c6b00..edf4dca414 100644 --- a/examples/example/scripts/controllerScriptingExamples.js +++ b/examples/example/scripts/controllerScriptingExamples.js @@ -29,6 +29,9 @@ if (hydra !== undefined) { var mapping = Controller.newMapping("Test"); var standard = Controller.Standard; print("standard:" + standard); + mapping.from(function () { return Math.sin(Date.now() / 250); }).to(function (newValue, oldValue, source) { + print("function source newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source); + }); mapping.from(hydra.L1).to(standard.A); mapping.from(hydra.L2).to(standard.B); mapping.from(hydra.L3).to(function (newValue, oldValue, source) { diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 68095505af..9f9ca85dd4 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -62,9 +62,7 @@ namespace controller { }; float ScriptEndpoint::value() { - if (QThread::currentThread() == thread()) { - updateValue(); - } + updateValue(); return _lastValue; } From fda8292fa60679419a715e20d048d7ce14e3898a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 14:53:23 -0700 Subject: [PATCH 056/232] remove dead code --- libraries/controllers/src/controllers/Endpoint.cpp | 4 ---- libraries/controllers/src/controllers/Endpoint.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/Endpoint.cpp index 5c5aa6cbc4..9e9b13f8ea 100644 --- a/libraries/controllers/src/controllers/Endpoint.cpp +++ b/libraries/controllers/src/controllers/Endpoint.cpp @@ -12,7 +12,3 @@ namespace controller { } -// FIXME - do we want to include the source Endpoint::Pointer in our calls to JS? If -// so we need to marshall this across the invokeMethod() properly -//static int EndpointPointerMetaTypeId = qRegisterMetaType("controller::Endpoint::Pointer"); - diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index f3cd163071..923412ac6c 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -54,8 +54,4 @@ namespace controller { }; } -// FIXME - do we want to include the source Endpoint::Pointer in our calls to JS? If -// so we need to marshall this across the invokeMethod() properly -//Q_DECLARE_METATYPE(controller::Endpoint::Pointer); - #endif From 57be59935d79e6594eced9e44320c133f93a967f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 17 Oct 2015 19:11:09 -0700 Subject: [PATCH 057/232] Move controller test to app --- .../resources/qml}/ScrollingGraph.qml | 2 +- interface/resources/qml/TestControllers.qml | 171 ++++++++++++++++++ .../qml/controller}/AnalogButton.qml | 2 +- .../resources/qml/controller}/AnalogStick.qml | 4 +- .../resources/qml/controller}/Hydra.qml | 3 +- .../qml/controller}/ToggleButton.qml | 2 +- .../resources/qml/controller}/Xbox.qml | 3 +- .../qml/controller}/hydra/HydraButtons.qml | 2 +- .../qml/controller}/hydra/HydraStick.qml | 2 +- .../resources/qml/controller}/hydra/hydra.png | Bin .../resources/qml/controller}/xbox/DPad.qml | 3 +- .../qml/controller}/xbox/LeftAnalogStick.qml | 2 +- .../qml/controller}/xbox/RightAnalogStick.qml | 2 +- .../qml/controller}/xbox/XboxButtons.qml | 2 +- .../xbox/xbox360-controller-md.png | Bin interface/src/Application.cpp | 112 +++++++----- interface/src/Application.h | 21 ++- .../src/controllers/ScriptingInterface.cpp | 76 ++++---- .../src/controllers/ScriptingInterface.h | 5 +- .../controllers/impl/MappingBuilderProxy.cpp | 7 +- .../controllers/impl/MappingBuilderProxy.h | 1 + .../controllers/impl/RouteBuilderProxy.cpp | 6 + .../src/controllers/impl/RouteBuilderProxy.h | 1 + .../src/EntityTreeRenderer.cpp | 3 +- .../render-utils/src/OffscreenQmlSurface.cpp | 6 + .../render-utils/src/OffscreenQmlSurface.h | 1 + .../src/AbstractScriptingServicesInterface.h | 11 -- libraries/script-engine/src/ScriptEngine.cpp | 11 +- libraries/script-engine/src/ScriptEngine.h | 2 - libraries/shared/src/DependencyManager.h | 22 ++- tests/controllers/qml/content.qml | 157 ---------------- tests/controllers/qml/main.qml | 10 +- tests/controllers/src/main.cpp | 36 +++- 33 files changed, 391 insertions(+), 297 deletions(-) rename {tests/controllers/qml/controls => interface/resources/qml}/ScrollingGraph.qml (98%) create mode 100644 interface/resources/qml/TestControllers.qml rename {tests/controllers/qml/controls => interface/resources/qml/controller}/AnalogButton.qml (95%) rename {tests/controllers/qml/controls => interface/resources/qml/controller}/AnalogStick.qml (92%) rename {tests/controllers/qml => interface/resources/qml/controller}/Hydra.qml (94%) rename {tests/controllers/qml/controls => interface/resources/qml/controller}/ToggleButton.qml (93%) rename {tests/controllers/qml => interface/resources/qml/controller}/Xbox.qml (95%) rename {tests/controllers/qml => interface/resources/qml/controller}/hydra/HydraButtons.qml (91%) rename {tests/controllers/qml => interface/resources/qml/controller}/hydra/HydraStick.qml (99%) rename {tests/controllers/qml => interface/resources/qml/controller}/hydra/hydra.png (100%) rename {tests/controllers/qml => interface/resources/qml/controller}/xbox/DPad.qml (96%) rename {tests/controllers/qml => interface/resources/qml/controller}/xbox/LeftAnalogStick.qml (92%) rename {tests/controllers/qml => interface/resources/qml/controller}/xbox/RightAnalogStick.qml (92%) rename {tests/controllers/qml => interface/resources/qml/controller}/xbox/XboxButtons.qml (97%) rename {tests/controllers/qml => interface/resources/qml/controller}/xbox/xbox360-controller-md.png (100%) delete mode 100644 tests/controllers/qml/content.qml diff --git a/tests/controllers/qml/controls/ScrollingGraph.qml b/interface/resources/qml/ScrollingGraph.qml similarity index 98% rename from tests/controllers/qml/controls/ScrollingGraph.qml rename to interface/resources/qml/ScrollingGraph.qml index 471d142d27..b5eaac6f89 100644 --- a/tests/controllers/qml/controls/ScrollingGraph.qml +++ b/interface/resources/qml/ScrollingGraph.qml @@ -22,7 +22,7 @@ Item { property string label: "" function update() { - value = Controllers.getValue(controlId); + value = Controller.getValue(controlId); canvas.requestPaint(); } diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml new file mode 100644 index 0000000000..388e452335 --- /dev/null +++ b/interface/resources/qml/TestControllers.qml @@ -0,0 +1,171 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "controller" +import "controls" as HifiControls +import "styles" + +HifiControls.VrDialog { + id: root + HifiConstants { id: hifi } + title: "Controller Test" + resizable: true + contentImplicitWidth: clientArea.implicitWidth + contentImplicitHeight: clientArea.implicitHeight + backgroundColor: "beige" + + property var actions: Controller.Actions + property var standard: Controller.Standard + property var hydra: null + property var testMapping: null + property var xbox: null + + + Component.onCompleted: { + enabled = true + var xboxRegex = /^X360Controller/; + var hydraRegex = /^Hydra/; + for (var prop in Controller.Hardware) { + if(xboxRegex.test(prop)) { + root.xbox = Controller.Hardware[prop] + print("found xbox") + continue + } + if (hydraRegex.test(prop)) { + root.hydra = Controller.Hardware[prop] + print("found hydra") + continue + } + } + } + + Column { + id: clientArea + spacing: 12 + x: root.clientX + y: root.clientY + + Row { + spacing: 8 + Button { + text: "Default Mapping" + onClicked: { + var mapping = Controller.newMapping("Default"); + mapping.from(xbox.A).to(standard.A); + mapping.from(xbox.B).to(standard.B); + mapping.from(xbox.X).to(standard.X); + mapping.from(xbox.Y).to(standard.Y); + mapping.from(xbox.Up).to(standard.DU); + mapping.from(xbox.Down).to(standard.DD); + mapping.from(xbox.Left).to(standard.DL); + mapping.from(xbox.Right).to(standard.Right); + mapping.from(xbox.LB).to(standard.LB); + mapping.from(xbox.RB).to(standard.RB); + mapping.from(xbox.LS).to(standard.LS); + mapping.from(xbox.RS).to(standard.RS); + mapping.from(xbox.Start).to(standard.Start); + mapping.from(xbox.Back).to(standard.Back); + mapping.from(xbox.LY).to(standard.LY); + mapping.from(xbox.LX).to(standard.LX); + mapping.from(xbox.RY).to(standard.RY); + mapping.from(xbox.RX).to(standard.RX); + mapping.from(xbox.LT).to(standard.LT); + mapping.from(xbox.RT).to(standard.RT); + Controller.enableMapping("Default"); + enabled = false; + text = "Built" + } + } + + Button { + text: "Build Mapping" + onClicked: { + var mapping = Controller.newMapping(); + // Inverting a value + mapping.from(hydra.RY).invert().to(standard.RY); + mapping.from(hydra.RX).to(standard.RX); + mapping.from(hydra.LY).to(standard.LY); + mapping.from(hydra.LY).to(standard.LX); + // Assigning a value from a function + // mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); + // Constrainting a value to -1, 0, or 1, with a deadzone +// mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); + mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); +// mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); +// mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); + // mapping.modifier(keyboard.Ctrl).scale(2.0) +// mapping.from(keyboard.A).to(actions.TranslateLeft) +// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) +// // First loopbacks +// // Then non-loopbacks by constraint level (number of inputs) +// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) +// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) + testMapping = mapping; + enabled = false + text = "Built" + } + } + + Button { + text: "Enable Mapping" + onClicked: root.testMapping.enable() + } + + Button { + text: "Disable Mapping" + onClicked: root.testMapping.disable() + } + + Button { + text: "Enable Mapping" + onClicked: print(Controller.getValue(root.xbox.LY)); + } + } + + Row { + Xbox { device: root.standard; label: "Standard"; width: 360 } + } + + Row { + spacing: 8 + Xbox { device: root.xbox; label: "XBox"; width: 360 } + Hydra { device: root.hydra; width: 360 } + } +// Row { +// spacing: 8 +// ScrollingGraph { +// controlId: Controller.Actions.Yaw +// label: "Yaw" +// min: -3.0 +// max: 3.0 +// size: 128 +// } +// +// ScrollingGraph { +// controlId: Controller.Actions.YAW_LEFT +// label: "Yaw Left" +// min: -3.0 +// max: 3.0 +// size: 128 +// } +// +// ScrollingGraph { +// controlId: Controller.Actions.YAW_RIGHT +// label: "Yaw Right" +// min: -3.0 +// max: 3.0 +// size: 128 +// } +// } + } +} // dialog + + + + + diff --git a/tests/controllers/qml/controls/AnalogButton.qml b/interface/resources/qml/controller/AnalogButton.qml similarity index 95% rename from tests/controllers/qml/controls/AnalogButton.qml rename to interface/resources/qml/controller/AnalogButton.qml index 141c131063..d027332d42 100644 --- a/tests/controllers/qml/controls/AnalogButton.qml +++ b/interface/resources/qml/controller/AnalogButton.qml @@ -13,7 +13,7 @@ Item { property color color: 'black' function update() { - value = Controllers.getValue(controlId); + value = Controller.getValue(controlId); canvas.requestPaint(); } diff --git a/tests/controllers/qml/controls/AnalogStick.qml b/interface/resources/qml/controller/AnalogStick.qml similarity index 92% rename from tests/controllers/qml/controls/AnalogStick.qml rename to interface/resources/qml/controller/AnalogStick.qml index 4e8ceb5736..499e0e9ce9 100644 --- a/tests/controllers/qml/controls/AnalogStick.qml +++ b/interface/resources/qml/controller/AnalogStick.qml @@ -17,8 +17,8 @@ Item { function update() { value = Qt.vector2d( - Controllers.getValue(controlIds[0]), - Controllers.getValue(controlIds[1]) + Controller.getValue(controlIds[0]), + Controller.getValue(controlIds[1]) ); if (root.invertY) { value.y = value.y * -1.0 diff --git a/tests/controllers/qml/Hydra.qml b/interface/resources/qml/controller/Hydra.qml similarity index 94% rename from tests/controllers/qml/Hydra.qml rename to interface/resources/qml/controller/Hydra.qml index 5ef47c4d83..19f3b4c193 100644 --- a/tests/controllers/qml/Hydra.qml +++ b/interface/resources/qml/controller/Hydra.qml @@ -3,8 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./hydra" -import "./controls" +import "hydra" Item { id: root diff --git a/tests/controllers/qml/controls/ToggleButton.qml b/interface/resources/qml/controller/ToggleButton.qml similarity index 93% rename from tests/controllers/qml/controls/ToggleButton.qml rename to interface/resources/qml/controller/ToggleButton.qml index 46a7b4bdfd..6481045dd0 100644 --- a/tests/controllers/qml/controls/ToggleButton.qml +++ b/interface/resources/qml/controller/ToggleButton.qml @@ -15,7 +15,7 @@ Item { Timer { interval: 50; running: true; repeat: true onTriggered: { - root.value = Controllers.getValue(root.controlId); + root.value = Controller.getValue(root.controlId); canvas.requestPaint(); } } diff --git a/tests/controllers/qml/Xbox.qml b/interface/resources/qml/controller/Xbox.qml similarity index 95% rename from tests/controllers/qml/Xbox.qml rename to interface/resources/qml/controller/Xbox.qml index bc9acd5a43..165ac596fe 100644 --- a/tests/controllers/qml/Xbox.qml +++ b/interface/resources/qml/controller/Xbox.qml @@ -3,8 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./xbox" -import "./controls" +import "xbox" Item { id: root diff --git a/tests/controllers/qml/hydra/HydraButtons.qml b/interface/resources/qml/controller/hydra/HydraButtons.qml similarity index 91% rename from tests/controllers/qml/hydra/HydraButtons.qml rename to interface/resources/qml/controller/hydra/HydraButtons.qml index 6c3070d2b1..aa8927f5b6 100644 --- a/tests/controllers/qml/hydra/HydraButtons.qml +++ b/interface/resources/qml/controller/hydra/HydraButtons.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./../controls" +import ".." Item { id: root diff --git a/tests/controllers/qml/hydra/HydraStick.qml b/interface/resources/qml/controller/hydra/HydraStick.qml similarity index 99% rename from tests/controllers/qml/hydra/HydraStick.qml rename to interface/resources/qml/controller/hydra/HydraStick.qml index 3c22789f6d..d082a20b10 100644 --- a/tests/controllers/qml/hydra/HydraStick.qml +++ b/interface/resources/qml/controller/hydra/HydraStick.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./../controls" +import ".." Item { id: root diff --git a/tests/controllers/qml/hydra/hydra.png b/interface/resources/qml/controller/hydra/hydra.png similarity index 100% rename from tests/controllers/qml/hydra/hydra.png rename to interface/resources/qml/controller/hydra/hydra.png diff --git a/tests/controllers/qml/xbox/DPad.qml b/interface/resources/qml/controller/xbox/DPad.qml similarity index 96% rename from tests/controllers/qml/xbox/DPad.qml rename to interface/resources/qml/controller/xbox/DPad.qml index 8efe6c2b30..2cfb6412e7 100644 --- a/tests/controllers/qml/xbox/DPad.qml +++ b/interface/resources/qml/controller/xbox/DPad.qml @@ -3,8 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./../controls" - +import ".." Item { id: root diff --git a/tests/controllers/qml/xbox/LeftAnalogStick.qml b/interface/resources/qml/controller/xbox/LeftAnalogStick.qml similarity index 92% rename from tests/controllers/qml/xbox/LeftAnalogStick.qml rename to interface/resources/qml/controller/xbox/LeftAnalogStick.qml index ed2689e7c8..8e2de1eb36 100644 --- a/tests/controllers/qml/xbox/LeftAnalogStick.qml +++ b/interface/resources/qml/controller/xbox/LeftAnalogStick.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./../controls" +import ".." Item { id: root diff --git a/tests/controllers/qml/xbox/RightAnalogStick.qml b/interface/resources/qml/controller/xbox/RightAnalogStick.qml similarity index 92% rename from tests/controllers/qml/xbox/RightAnalogStick.qml rename to interface/resources/qml/controller/xbox/RightAnalogStick.qml index 611b4d8f92..0cdfeda2cf 100644 --- a/tests/controllers/qml/xbox/RightAnalogStick.qml +++ b/interface/resources/qml/controller/xbox/RightAnalogStick.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./../controls" +import ".." Item { id: root diff --git a/tests/controllers/qml/xbox/XboxButtons.qml b/interface/resources/qml/controller/xbox/XboxButtons.qml similarity index 97% rename from tests/controllers/qml/xbox/XboxButtons.qml rename to interface/resources/qml/controller/xbox/XboxButtons.qml index 4a9e87799e..e26a4a0b98 100644 --- a/tests/controllers/qml/xbox/XboxButtons.qml +++ b/interface/resources/qml/controller/xbox/XboxButtons.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -import "./../controls" +import ".." Item { id: root diff --git a/tests/controllers/qml/xbox/xbox360-controller-md.png b/interface/resources/qml/controller/xbox/xbox360-controller-md.png similarity index 100% rename from tests/controllers/qml/xbox/xbox360-controller-md.png rename to interface/resources/qml/controller/xbox/xbox360-controller-md.png diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c01e89c6d9..b0c0109b71 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -17,27 +17,32 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include #include #include @@ -120,6 +125,7 @@ #include "scripting/SettingsScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" +#include "scripting/ControllerScriptingInterface.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -316,6 +322,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + #if defined(Q_OS_MAC) || defined(Q_OS_WIN) DependencyManager::set(); #endif @@ -327,7 +334,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - + DependencyManager::set(); return true; } @@ -374,6 +381,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : setInstance(this); + auto controllerScriptingInterface = DependencyManager::get().data(); + _controllerScriptingInterface = dynamic_cast(controllerScriptingInterface); // to work around the Qt constant wireless scanning, set the env for polling interval very high const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); @@ -618,7 +627,7 @@ 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, _controllerScriptingInterface, &ControllerScriptingInterface::actionEvent); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { if (state) { switch (action) { @@ -975,6 +984,8 @@ void Application::initializeUi() { offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->load("Root.qml"); offscreenUi->load("RootMenu.qml"); + auto scriptingInterface = DependencyManager::get(); + offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data()); _glWidget->installEventFilter(offscreenUi.data()); VrMenu::load(); VrMenu::executeQueuedLambdas(); @@ -1456,7 +1467,7 @@ bool Application::event(QEvent* event) { } if (HFActionEvent::types().contains(event->type())) { - _controllerScriptingInterface.handleMetaEvent(static_cast(event)); + _controllerScriptingInterface->handleMetaEvent(static_cast(event)); } return QApplication::event(event); @@ -1470,7 +1481,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) { } // Filter out captured keys before they're used for shortcut actions. - if (_controllerScriptingInterface.isKeyCaptured(static_cast(event))) { + if (_controllerScriptingInterface->isKeyCaptured(static_cast(event))) { event->accept(); return true; } @@ -1485,10 +1496,10 @@ void Application::keyPressEvent(QKeyEvent* event) { _altPressed = event->key() == Qt::Key_Alt; _keysPressed.insert(event->key()); - _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts + _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isKeyCaptured(event)) { + if (_controllerScriptingInterface->isKeyCaptured(event)) { return; } @@ -1518,6 +1529,13 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isMeta) { auto offscreenUi = DependencyManager::get(); offscreenUi->load("Browser.qml"); + } + break; + + case Qt::Key_X: + if (isMeta && isShifted) { + auto offscreenUi = DependencyManager::get(); + offscreenUi->load("TestControllers.qml"); } break; @@ -1752,10 +1770,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _keysPressed.remove(event->key()); - _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts + _controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isKeyCaptured(event)) { + if (_controllerScriptingInterface->isKeyCaptured(event)) { return; } @@ -1844,10 +1862,10 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { _entities.mouseMoveEvent(&mappedEvent, deviceID); - _controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts + _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isMouseCaptured()) { + if (_controllerScriptingInterface->isMouseCaptured()) { return; } @@ -1872,10 +1890,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { _entities.mousePressEvent(&mappedEvent, deviceID); } - _controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts + _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isMouseCaptured()) { + if (_controllerScriptingInterface->isMouseCaptured()) { return; } @@ -1897,11 +1915,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isMouseCaptured()) { + if (_controllerScriptingInterface->isMouseCaptured()) { return; } - _controllerScriptingInterface.emitMouseDoublePressEvent(event); + _controllerScriptingInterface->emitMouseDoublePressEvent(event); } void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1917,10 +1935,10 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { _entities.mouseReleaseEvent(&mappedEvent, deviceID); } - _controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts + _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isMouseCaptured()) { + if (_controllerScriptingInterface->isMouseCaptured()) { return; } @@ -1943,12 +1961,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) { if (event->type() == QEvent::TouchUpdate) { TouchEvent thisEvent(*event, _lastTouchEvent); - _controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts + _controllerScriptingInterface->emitTouchUpdateEvent(thisEvent); // send events to any registered scripts _lastTouchEvent = thisEvent; } // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isTouchCaptured()) { + if (_controllerScriptingInterface->isTouchCaptured()) { return; } @@ -1960,13 +1978,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) { void Application::touchBeginEvent(QTouchEvent* event) { _altPressed = false; TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event - _controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts + _controllerScriptingInterface->emitTouchBeginEvent(thisEvent); // send events to any registered scripts _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update touchUpdateEvent(event); // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isTouchCaptured()) { + if (_controllerScriptingInterface->isTouchCaptured()) { return; } @@ -1979,11 +1997,11 @@ void Application::touchBeginEvent(QTouchEvent* event) { void Application::touchEndEvent(QTouchEvent* event) { _altPressed = false; TouchEvent thisEvent(*event, _lastTouchEvent); - _controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts + _controllerScriptingInterface->emitTouchEndEvent(thisEvent); // send events to any registered scripts _lastTouchEvent = thisEvent; // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isTouchCaptured()) { + if (_controllerScriptingInterface->isTouchCaptured()) { return; } @@ -1996,10 +2014,10 @@ void Application::touchEndEvent(QTouchEvent* event) { void Application::wheelEvent(QWheelEvent* event) { _altPressed = false; - _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts + _controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it - if (_controllerScriptingInterface.isWheelCaptured()) { + if (_controllerScriptingInterface->isWheelCaptured()) { return; } @@ -2709,12 +2727,12 @@ void Application::update(float deltaTime) { } // Dispatch input events - _controllerScriptingInterface.update(); + _controllerScriptingInterface->update(); // Transfer the user inputs to the driveKeys myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { - if (!_controllerScriptingInterface.areActionsCaptured()) { + if (!_controllerScriptingInterface->areActionsCaptured()) { myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP)); diff --git a/interface/src/Application.h b/interface/src/Application.h index 61421c34d6..829265c9fb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -14,13 +14,15 @@ #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include + +#include +#include #include #include @@ -161,7 +163,7 @@ public: ToolWindow* getToolWindow() { return _toolWindow ; } - virtual controller::ScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; } + virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; } virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); QImage renderAvatarBillboard(RenderArgs* renderArgs); @@ -474,8 +476,7 @@ private: NodeToJurisdictionMap _entityServerJurisdictions; NodeToOctreeSceneStats _octreeServerSceneStats; - - ControllerScriptingInterface _controllerScriptingInterface; + ControllerScriptingInterface* _controllerScriptingInterface{ nullptr }; QPointer _logDialog; QPointer _snapshotShareDialog; diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 9f9ca85dd4..6d4ad1a566 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -26,7 +26,6 @@ #include "Logging.h" #include "InputDevice.h" - namespace controller { class VirtualEndpoint : public Endpoint { @@ -144,39 +143,9 @@ namespace controller { } return deviceMap; } + - ScriptingInterface::ScriptingInterface() { - auto userInputMapper = DependencyManager::get(); - qCDebug(controllers) << "Setting up standard controller abstraction"; - auto standardDevice = userInputMapper->getStandardDevice(); - // Expose the IDs to JS - _standard = createDeviceMap(standardDevice.get()); - // Create the endpoints - for (const auto& inputMapping : standardDevice->getAvailabeInputs()) { - const auto& standardInput = inputMapping.first; - // Ignore aliases - if (_endpoints.count(standardInput)) { - continue; - } - _endpoints[standardInput] = std::make_shared(standardInput); - } - - // FIXME allow custom user actions? - auto actionNames = userInputMapper->getActionNames(); - int actionNumber = 0; - qCDebug(controllers) << "Setting up standard actions"; - for (const auto& actionName : actionNames) { - UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); - qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); - // Expose the IDs to JS - QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); - _actions.insert(cleanActionName, actionInput.getID()); - - // Create the action endpoints - _endpoints[actionInput] = std::make_shared(actionInput); - } - - updateMaps(); + ScriptingInterface::~ScriptingInterface() { } QObject* ScriptingInterface::newMapping(const QString& mappingName) { @@ -235,7 +204,7 @@ namespace controller { if (request->getResult() == ResourceRequest::Success) { result = parseMapping(QString(request->getData())); } else { - qDebug() << "Failed to load mapping url <" << jsonUrl << ">" << endl; + qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl; } request->deleteLater(); } @@ -245,6 +214,7 @@ namespace controller { Q_INVOKABLE QObject* newMapping(const QJsonObject& json); void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) { + qCDebug(controllers) << "Attempting to enable mapping " << mappingName; auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; @@ -524,9 +494,45 @@ namespace controller { } } } - } // namespace controllers + +using namespace controller; +// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block +ScriptingInterface::ScriptingInterface() { + auto userInputMapper = DependencyManager::get(); + qCDebug(controllers) << "Setting up standard controller abstraction"; + auto standardDevice = userInputMapper->getStandardDevice(); + // Expose the IDs to JS + _standard = createDeviceMap(standardDevice.get()); + // Create the endpoints + for (const auto& inputMapping : standardDevice->getAvailabeInputs()) { + const auto& standardInput = inputMapping.first; + // Ignore aliases + if (_endpoints.count(standardInput)) { + continue; + } + _endpoints[standardInput] = std::make_shared(standardInput); + } + + // FIXME allow custom user actions? + auto actionNames = userInputMapper->getActionNames(); + int actionNumber = 0; + qCDebug(controllers) << "Setting up standard actions"; + for (const auto& actionName : actionNames) { + UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); + qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); + // Expose the IDs to JS + QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); + _actions.insert(cleanActionName, actionInput.getID()); + + // Create the action endpoints + _endpoints[actionInput] = std::make_shared(actionInput); + } + + updateMaps(); +} + //var mapping = Controller.newMapping(); //mapping.map(hydra.LeftButton0, actions.ContextMenu); //mapping.map(hydra.LeftButton0).to(xbox.RT); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 07ddd625f5..ef9d61c32d 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -28,6 +28,8 @@ #include #include +#include + #include "UserInputMapper.h" #include "StandardControls.h" #include "Mapping.h" @@ -55,7 +57,7 @@ namespace controller { }; /// handles scripting of input controller commands from JS - class ScriptingInterface : public QObject { + class ScriptingInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL) Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL) @@ -63,6 +65,7 @@ namespace controller { public: ScriptingInterface(); + virtual ~ScriptingInterface(); Q_INVOKABLE float getValue(const int& source) const; Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const; diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index c6031d45d2..e75068b311 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -14,13 +14,18 @@ #include #include - #include "RouteBuilderProxy.h" #include "../ScriptingInterface.h" #include "../Logging.h" using namespace controller; +QObject* MappingBuilderProxy::from(int input) { + qCDebug(controllers) << "Creating new Route builder proxy from " << input; + auto sourceEndpoint = _parent.endpointFor(UserInputMapper::Input(input)); + return from(sourceEndpoint); +} + QObject* MappingBuilderProxy::from(const QJSValue& source) { qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); auto sourceEndpoint = _parent.endpointFor(source); diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index a29f7ade39..53db901436 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -31,6 +31,7 @@ public: MappingBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping) : _parent(parent), _mapping(mapping) { } + Q_INVOKABLE QObject* from(int sourceInput); Q_INVOKABLE QObject* from(const QJSValue& source); Q_INVOKABLE QObject* from(const QScriptValue& source); Q_INVOKABLE QObject* makeAxis(const QJSValue& source1, const QJSValue& source2); diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 033e94daa0..ba2cd60c8b 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -20,6 +20,12 @@ namespace controller { +void RouteBuilderProxy::to(int destinationInput) { + qCDebug(controllers) << "Completing route " << destinationInput; + auto destinationEndpoint = _parent.endpointFor(UserInputMapper::Input(destinationInput)); + return to(destinationEndpoint); +} + void RouteBuilderProxy::to(const QJSValue& destination) { qCDebug(controllers) << "Completing route " << destination.toString(); auto destinationEndpoint = _parent.endpointFor(destination); diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 6fd5f1467a..8e3b3404cc 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -30,6 +30,7 @@ class RouteBuilderProxy : public QObject { RouteBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route) : _parent(parent), _mapping(mapping), _route(route) { } + Q_INVOKABLE void to(int destination); Q_INVOKABLE void to(const QJSValue& destination); Q_INVOKABLE void to(const QScriptValue& destination); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 8f316af276..5ac8ce8f13 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -106,8 +106,7 @@ void EntityTreeRenderer::init() { entityTree->setFBXService(this); if (_wantScripts) { - _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities", - _scriptingServices->getControllerScriptingInterface()); + _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities"); _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); _entitiesScriptEngine->runInThread(); DependencyManager::get()->setEntitiesScriptEngine(_entitiesScriptEngine); diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 6c68b60f42..b53ff37436 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include "GLEscrow.h" @@ -614,3 +615,8 @@ QQuickWindow* OffscreenQmlSurface::getWindow() { QSize OffscreenQmlSurface::size() const { return _renderer->_quickWindow->geometry().size(); } + +QQmlContext* OffscreenQmlSurface::getRootContext() { + return _qmlEngine->rootContext(); +} + diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/render-utils/src/OffscreenQmlSurface.h index 01dd2b88f9..67315b0783 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.h +++ b/libraries/render-utils/src/OffscreenQmlSurface.h @@ -60,6 +60,7 @@ public: QQuickItem* getRootItem(); QQuickWindow* getWindow(); QObject* getEventHandler(); + QQmlContext* getRootContext(); QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget); virtual bool eventFilter(QObject* originalDestination, QEvent* event); diff --git a/libraries/script-engine/src/AbstractScriptingServicesInterface.h b/libraries/script-engine/src/AbstractScriptingServicesInterface.h index 565a415f63..de18338fca 100644 --- a/libraries/script-engine/src/AbstractScriptingServicesInterface.h +++ b/libraries/script-engine/src/AbstractScriptingServicesInterface.h @@ -12,24 +12,13 @@ #ifndef hifi_AbstractScriptingServicesInterface_h #define hifi_AbstractScriptingServicesInterface_h -namespace controller { - class ScriptingInterface; -} - -class Transform; class ScriptEngine; -class QThread; /// Interface provided by Application to other objects that need access to scripting services of the application class AbstractScriptingServicesInterface { public: - - /// Returns the controller interface for the application - virtual controller::ScriptingInterface* getControllerScriptingInterface() = 0; - /// Registers application specific services with a script engine. virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0; - }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 494f038a61..0dfc2fe09e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -84,8 +84,7 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu out = qobject_cast(object.toQObject()); } -ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, - controller::ScriptingInterface* controllerScriptingInterface, bool wantSignals) : +ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) : _scriptContents(scriptContents), _isFinished(false), @@ -93,7 +92,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _isInitialized(false), _timerFunctionMap(), _wantSignals(wantSignals), - _controllerScriptingInterface(controllerScriptingInterface), _fileNameString(fileNameString), _quatLibrary(), _vec3Library(), @@ -310,7 +308,8 @@ void ScriptEngine::init() { registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); - registerGlobalObject("Controller", _controllerScriptingInterface); + auto scriptingInterface = DependencyManager::get(); + registerGlobalObject("Controller", scriptingInterface.data()); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); @@ -320,8 +319,8 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - if (_controllerScriptingInterface) { - _controllerScriptingInterface->registerControllerTypes(this); + if (scriptingInterface) { + scriptingInterface->registerControllerTypes(this); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 642159339e..5a0fb64ae2 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -55,7 +55,6 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""), - controller::ScriptingInterface* controllerScriptingInterface = nullptr, bool wantSignals = true); ~ScriptEngine(); @@ -184,7 +183,6 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - controller::ScriptingInterface* _controllerScriptingInterface; QString _fileNameString; Quat _quatLibrary; Vec3 _vec3Library; diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index 01b755fdd0..db41e72e1e 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -51,7 +51,10 @@ public: template static QSharedPointer set(Args&&... args); - + + template + static QSharedPointer set(Args&&... args); + template static void destroy(); @@ -89,13 +92,26 @@ QSharedPointer DependencyManager::get() { template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = _manager.getHashCode(); - + QSharedPointer& instance = _manager.safeGet(hashCode); instance.clear(); // Clear instance before creation of new one to avoid edge cases QSharedPointer newInstance(new T(args...), &T::customDeleter); QSharedPointer storedInstance = qSharedPointerCast(newInstance); instance.swap(storedInstance); - + + return newInstance; +} + +template +QSharedPointer DependencyManager::set(Args&&... args) { + static size_t hashCode = _manager.getHashCode(); + + QSharedPointer& instance = _manager.safeGet(hashCode); + instance.clear(); // Clear instance before creation of new one to avoid edge cases + QSharedPointer newInstance(new I(args...), &I::customDeleter); + QSharedPointer storedInstance = qSharedPointerCast(newInstance); + instance.swap(storedInstance); + return newInstance; } diff --git a/tests/controllers/qml/content.qml b/tests/controllers/qml/content.qml deleted file mode 100644 index 5d81ead2fd..0000000000 --- a/tests/controllers/qml/content.qml +++ /dev/null @@ -1,157 +0,0 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -import "./xbox" -import "./controls" - -Column { - id: root - property var actions: Controllers.Actions - property var standard: Controllers.Standard - property var hydra: null - property var testMapping: null - property var xbox: null - - - Component.onCompleted: { - var xboxRegex = /^X360Controller/; - var hydraRegex = /^Hydra/; - for (var prop in Controllers.Hardware) { - if(xboxRegex.test(prop)) { - root.xbox = Controllers.Hardware[prop] - print("found xbox") - continue - } - if (hydraRegex.test(prop)) { - root.hydra = Controllers.Hardware[prop] - print("found hydra") - continue - } - } - } - - spacing: 12 - - Timer { - interval: 50; running: true; repeat: true - onTriggered: { - Controllers.update(); - } - } - - Row { - spacing: 8 - Button { - text: "Default Mapping" - onClicked: { - var mapping = Controllers.newMapping("Default"); - mapping.from(xbox.A).to(standard.A); - mapping.from(xbox.B).to(standard.B); - mapping.from(xbox.X).to(standard.X); - mapping.from(xbox.Y).to(standard.Y); - mapping.from(xbox.Up).to(standard.DU); - mapping.from(xbox.Down).to(standard.DD); - mapping.from(xbox.Left).to(standard.DL); - mapping.from(xbox.Right).to(standard.Right); - mapping.from(xbox.LB).to(standard.LB); - mapping.from(xbox.RB).to(standard.RB); - mapping.from(xbox.LS).to(standard.LS); - mapping.from(xbox.RS).to(standard.RS); - mapping.from(xbox.Start).to(standard.Start); - mapping.from(xbox.Back).to(standard.Back); - mapping.from(xbox.LY).to(standard.LY); - mapping.from(xbox.LX).to(standard.LX); - mapping.from(xbox.RY).to(standard.RY); - mapping.from(xbox.RX).to(standard.RX); - mapping.from(xbox.LT).to(standard.LT); - mapping.from(xbox.RT).to(standard.RT); - Controllers.enableMapping("Default"); - enabled = false; - text = "Built" - } - } - - Button { - text: "Build Mapping" - onClicked: { - var mapping = Controllers.newMapping(); - // Inverting a value - mapping.from(xbox.RY).invert().to(standard.RY); - // Assigning a value from a function - mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); - // Constrainting a value to -1, 0, or 1, with a deadzone - mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); - mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); - mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); - mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); - // mapping.modifier(keyboard.Ctrl).scale(2.0) -// mapping.from(keyboard.A).to(actions.TranslateLeft) -// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) -// // First loopbacks -// // Then non-loopbacks by constraint level (number of inputs) -// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) -// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) - testMapping = mapping; - enabled = false - text = "Built" - } - } - - Button { - text: "Enable Mapping" - onClicked: root.testMapping.enable() - } - - Button { - text: "Disable Mapping" - onClicked: root.testMapping.disable() - } - } - - Row { - Xbox { device: root.standard; label: "Standard"; width: 360 } - } - - Row { - spacing: 8 - Xbox { device: root.xbox; label: "XBox"; width: 360 } - } - - Row { - spacing: 8 - Hydra { device: root.hydra; width: 360 } - } - - Row { - spacing: 8 - ScrollingGraph { - controlId: Controllers.Actions.Yaw - label: "Yaw" - min: -3.0 - max: 3.0 - size: 128 - } - - ScrollingGraph { - controlId: Controllers.Actions.YAW_LEFT - label: "Yaw Left" - min: -3.0 - max: 3.0 - size: 128 - } - - ScrollingGraph { - controlId: Controllers.Actions.YAW_RIGHT - label: "Yaw Right" - min: -3.0 - max: 3.0 - size: 128 - } - } -} - diff --git a/tests/controllers/qml/main.qml b/tests/controllers/qml/main.qml index 5ed68cc1fb..66060399a6 100644 --- a/tests/controllers/qml/main.qml +++ b/tests/controllers/qml/main.qml @@ -7,8 +7,16 @@ ApplicationWindow { id: window visible: true + Timer { + interval: 50; running: true; repeat: true + onTriggered: { + Controller.update(); + } + } + + Loader { id: pageLoader - source: "content.qml" + source: ResourcePath + "TestControllers.qml" } } diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 0c5ef09867..ccd670640c 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -43,17 +43,37 @@ #include #include -const QString& getQmlDir() { +const QString& getResourcesDir() { static QString dir; if (dir.isEmpty()) { QDir path(__FILE__); path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../qml/")) + "/"; + dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; + qDebug() << "Resources Path: " << dir; + } + return dir; +} + +const QString& getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + dir = getResourcesDir() + "qml/"; qDebug() << "Qml Path: " << dir; } return dir; } +const QString& getTestQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../")) + "/qml/"; + qDebug() << "Qml Test Path: " << dir; + } + return dir; +} + using namespace controller; @@ -88,6 +108,7 @@ public: int main(int argc, char** argv) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; + auto rootContext = engine.rootContext(); new PluginContainerProxy(); // Simulate our application idle loop @@ -119,11 +140,16 @@ int main(int argc, char** argv) { } inputPlugin->pluginUpdate(0, false); } - auto rootContext = engine.rootContext(); rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); } - - engine.load(getQmlDir() + "main.qml"); + qDebug() << getQmlDir(); + rootContext->setContextProperty("ResourcePath", getQmlDir()); + engine.setBaseUrl(QUrl::fromLocalFile(getQmlDir())); + engine.addImportPath(getQmlDir()); + engine.load(getTestQmlDir() + "main.qml"); + for (auto pathItem : engine.importPathList()) { + qDebug() << pathItem; + } app.exec(); return 0; } From 734a39f962916e49dfeaa0702a0d13f57d4c1c6d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 18 Oct 2015 18:58:03 -0700 Subject: [PATCH 058/232] Breaking up UserInputMapper, restoring some mappings --- interface/resources/qml/TestControllers.qml | 2 +- .../ControllerScriptingInterface.cpp | 315 ------------------ .../scripting/ControllerScriptingInterface.h | 47 +-- .../src/controllers/DeviceProxy.cpp | 31 ++ .../controllers/src/controllers/DeviceProxy.h | 57 ++++ .../controllers/src/controllers/Input.cpp | 19 ++ libraries/controllers/src/controllers/Input.h | 76 +++++ .../src/controllers/InputDevice.cpp | 4 +- .../controllers/src/controllers/InputDevice.h | 8 +- .../controllers/src/controllers/Pose.cpp | 30 ++ libraries/controllers/src/controllers/Pose.h | 43 +++ .../src/controllers/ScriptingInterface.cpp | 125 ++++--- .../src/controllers/ScriptingInterface.h | 39 ++- .../src/controllers/StandardController.cpp | 61 +++- .../src/controllers/UserInputMapper.cpp | 184 ++++++---- .../src/controllers/UserInputMapper.h | 111 +----- .../src/input-plugins/Joystick.cpp | 60 ++++ .../src/input-plugins/Joystick.h | 1 + .../src/input-plugins/SixenseManager.cpp | 75 ++++- .../src/input-plugins/SixenseManager.h | 2 + libraries/script-engine/src/ScriptEngine.cpp | 10 +- 21 files changed, 670 insertions(+), 630 deletions(-) create mode 100644 libraries/controllers/src/controllers/DeviceProxy.cpp create mode 100644 libraries/controllers/src/controllers/DeviceProxy.h create mode 100644 libraries/controllers/src/controllers/Input.cpp create mode 100644 libraries/controllers/src/controllers/Input.h create mode 100644 libraries/controllers/src/controllers/Pose.cpp create mode 100644 libraries/controllers/src/controllers/Pose.h diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index 388e452335..d8c9cb4343 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -87,7 +87,7 @@ HifiControls.VrDialog { mapping.from(hydra.RY).invert().to(standard.RY); mapping.from(hydra.RX).to(standard.RX); mapping.from(hydra.LY).to(standard.LY); - mapping.from(hydra.LY).to(standard.LX); + mapping.from(hydra.LX).to(standard.LX); // Assigning a value from a function // mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); // Constrainting a value to -1, 0, or 1, with a deadzone diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index f1f7b08587..931c97a7d2 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -23,109 +23,6 @@ // TODO: this needs to be removed, as well as any related controller-specific information #include -ControllerScriptingInterface::ControllerScriptingInterface() : - _mouseCaptured(false), - _touchCaptured(false), - _wheelCaptured(false), - _actionsCaptured(false) -{ - -} - -ControllerScriptingInterface::~ControllerScriptingInterface() { -} - - -static int actionMetaTypeId = qRegisterMetaType(); -static int inputChannelMetaTypeId = qRegisterMetaType(); -static int inputMetaTypeId = qRegisterMetaType(); -static int inputPairMetaTypeId = qRegisterMetaType(); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); -void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); -QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel); -void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel); -QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); -void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); -void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { - QScriptValue obj = engine->newObject(); - obj.setProperty("device", input.getDevice()); - obj.setProperty("channel", input.getChannel()); - obj.setProperty("type", (unsigned short) input.getType()); - obj.setProperty("id", input.getID()); - return obj; -} - -void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { - input.setDevice(object.property("device").toUInt16()); - input.setChannel(object.property("channel").toUInt16()); - input.setType(object.property("type").toUInt16()); - input.setID(object.property("id").toInt32()); -} - -QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) { - QScriptValue obj = engine->newObject(); - obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput())); - obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier())); - obj.setProperty("action", inputChannel.getAction()); - obj.setProperty("scale", inputChannel.getScale()); - return obj; -} - -void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) { - UserInputMapper::Input input; - UserInputMapper::Input modifier; - inputFromScriptValue(object.property("input"), input); - inputChannel.setInput(input); - inputFromScriptValue(object.property("modifier"), modifier); - inputChannel.setModifier(modifier); - inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt())); - inputChannel.setScale(object.property("scale").toVariant().toFloat()); -} - -QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { - QScriptValue obj = engine->newObject(); - auto userInputMapper = DependencyManager::get(); - QVector inputChannels = userInputMapper->getInputChannelsForAction(action); - QScriptValue _inputChannels = engine->newArray(inputChannels.size()); - for (int i = 0; i < inputChannels.size(); i++) { - _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i])); - } - obj.setProperty("action", (int) action); - obj.setProperty("actionName", userInputMapper->getActionName(action)); - obj.setProperty("inputChannels", _inputChannels); - return obj; -} - -void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { - action = UserInputMapper::Action(object.property("action").toVariant().toInt()); -} - -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { - QScriptValue obj = engine->newObject(); - obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); - obj.setProperty("inputName", inputPair.second); - return obj; -} - -void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { - inputFromScriptValue(object.property("input"), inputPair.first); - inputPair.second = QString(object.property("inputName").toVariant().toString()); -} - -void ControllerScriptingInterface::registerControllerTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); - qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue); - qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); - qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); -} - void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { emit actionStartEvent(static_cast(*event)); @@ -183,162 +80,6 @@ const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const return NULL; } -/* -bool ControllerScriptingInterface::isPrimaryButtonPressed() const { - const PalmData* primaryPalm = getPrimaryPalm(); - if (primaryPalm) { - if (primaryPalm->getControllerButtons() & BUTTON_FWD) { - return true; - } - } - - return false; -} - -glm::vec2 ControllerScriptingInterface::getPrimaryJoystickPosition() const { - const PalmData* primaryPalm = getPrimaryPalm(); - if (primaryPalm) { - return glm::vec2(primaryPalm->getJoystickX(), primaryPalm->getJoystickY()); - } - - return glm::vec2(0); -} - -int ControllerScriptingInterface::getNumberOfButtons() const { - return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM; -} - -bool ControllerScriptingInterface::isButtonPressed(int buttonIndex) const { - int palmIndex = buttonIndex / NUMBER_OF_BUTTONS_PER_PALM; - int buttonOnPalm = buttonIndex % NUMBER_OF_BUTTONS_PER_PALM; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - switch (buttonOnPalm) { - case 0: - return palmData->getControllerButtons() & BUTTON_0; - case 1: - return palmData->getControllerButtons() & BUTTON_1; - case 2: - return palmData->getControllerButtons() & BUTTON_2; - case 3: - return palmData->getControllerButtons() & BUTTON_3; - case 4: - return palmData->getControllerButtons() & BUTTON_4; - case 5: - return palmData->getControllerButtons() & BUTTON_FWD; - } - } - return false; -} - -int ControllerScriptingInterface::getNumberOfTriggers() const { - return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM; -} - -float ControllerScriptingInterface::getTriggerValue(int triggerIndex) const { - // we know there's one trigger per palm, so the triggerIndex is the palm Index - int palmIndex = triggerIndex; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - return palmData->getTrigger(); - } - return 0.0f; -} - -int ControllerScriptingInterface::getNumberOfJoysticks() const { - return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM; -} - -glm::vec2 ControllerScriptingInterface::getJoystickPosition(int joystickIndex) const { - // we know there's one joystick per palm, so the joystickIndex is the palm Index - int palmIndex = joystickIndex; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - return glm::vec2(palmData->getJoystickX(), palmData->getJoystickY()); - } - return glm::vec2(0); -} - -int ControllerScriptingInterface::getNumberOfSpatialControls() const { - return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM; -} - -glm::vec3 ControllerScriptingInterface::getSpatialControlPosition(int controlIndex) const { - int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; - int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - switch (controlOfPalm) { - case PALM_SPATIALCONTROL: - return palmData->getPosition(); - case TIP_SPATIALCONTROL: - return palmData->getTipPosition(); - } - } - return glm::vec3(0); // bad index -} - -glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlIndex) const { - int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; - int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - switch (controlOfPalm) { - case PALM_SPATIALCONTROL: - return palmData->getVelocity(); - case TIP_SPATIALCONTROL: - return palmData->getTipVelocity(); - } - } - return glm::vec3(0); // bad index -} - -glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { - int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; - int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - switch (controlOfPalm) { - case PALM_SPATIALCONTROL: - return palmData->getRawRotation(); - case TIP_SPATIALCONTROL: - return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation - } - } - return glm::quat(); // bad index -} - -glm::vec3 ControllerScriptingInterface::getSpatialControlRawAngularVelocity(int controlIndex) const { - int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; - int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - switch (controlOfPalm) { - case PALM_SPATIALCONTROL: - return palmData->getRawAngularVelocity(); - case TIP_SPATIALCONTROL: - return palmData->getRawAngularVelocity(); // Tip = palm angular velocity - } - } - return glm::vec3(0); // bad index -} - -glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const { - int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; - int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; - const PalmData* palmData = getActivePalm(palmIndex); - if (palmData) { - switch (controlOfPalm) { - case PALM_SPATIALCONTROL: - return palmData->getNormal(); - case TIP_SPATIALCONTROL: - return palmData->getFingerDirection(); - } - } - return glm::vec3(0); // bad index -} -*/ - bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { return isKeyCaptured(KeyEvent(*event)); } @@ -448,62 +189,6 @@ void ControllerScriptingInterface::update() { controller::ScriptingInterface::update(); } -QVector ControllerScriptingInterface::getAllActions() { - return DependencyManager::get()->getAllActions(); -} - -QVector ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) { - return DependencyManager::get()->getInputChannelsForAction(action); -} - -QString ControllerScriptingInterface::getDeviceName(unsigned int device) { - return DependencyManager::get()->getDeviceName((unsigned short)device); -} - -QVector ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) { - return DependencyManager::get()->getAllInputsForDevice(device); -} - -bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) { - return DependencyManager::get()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale); -} - -bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) { - return DependencyManager::get()->removeInputChannel(inputChannel); -} - -QVector ControllerScriptingInterface::getAvailableInputs(unsigned int device) { - return DependencyManager::get()->getAvailableInputs((unsigned short)device); -} - -void ControllerScriptingInterface::resetAllDeviceBindings() { - DependencyManager::get()->resetAllDeviceBindings(); -} - -void ControllerScriptingInterface::resetDevice(unsigned int device) { - DependencyManager::get()->resetDevice(device); -} - -int ControllerScriptingInterface::findDevice(QString name) { - return DependencyManager::get()->findDevice(name); -} - -QVector ControllerScriptingInterface::getDeviceNames() { - return DependencyManager::get()->getDeviceNames(); -} - -float ControllerScriptingInterface::getActionValue(int action) { - return DependencyManager::get()->getActionState(UserInputMapper::Action(action)); -} - -int ControllerScriptingInterface::findAction(QString actionName) { - return DependencyManager::get()->findAction(actionName); -} - -QVector ControllerScriptingInterface::getActionNames() const { - return DependencyManager::get()->getActionNames(); -} - InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : _deviceTrackerId(deviceTrackerId), _subTrackerId(subTrackerId), diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 25d9a523d3..24065e6799 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -63,33 +63,8 @@ class ControllerScriptingInterface : public controller::ScriptingInterface { public: - ControllerScriptingInterface(); - ~ControllerScriptingInterface(); + virtual ~ControllerScriptingInterface() {} - Q_INVOKABLE QVector getAllActions(); - - Q_INVOKABLE bool addInputChannel(UserInputMapper::InputChannel inputChannel); - Q_INVOKABLE bool removeInputChannel(UserInputMapper::InputChannel inputChannel); - Q_INVOKABLE QVector getInputChannelsForAction(UserInputMapper::Action action); - - Q_INVOKABLE QVector getAvailableInputs(unsigned int device); - Q_INVOKABLE QVector getAllInputsForDevice(unsigned int device); - - Q_INVOKABLE QString getDeviceName(unsigned int device); - - Q_INVOKABLE float getActionValue(int action); - - Q_INVOKABLE void resetDevice(unsigned int device); - Q_INVOKABLE void resetAllDeviceBindings(); - Q_INVOKABLE int findDevice(QString name); - Q_INVOKABLE QVector getDeviceNames(); - - Q_INVOKABLE int findAction(QString actionName); - Q_INVOKABLE QVector getActionNames() const; - - - virtual void registerControllerTypes(QScriptEngine* engine); - void emitKeyPressEvent(QKeyEvent* event); void emitKeyReleaseEvent(QKeyEvent* event); @@ -108,10 +83,6 @@ public: bool isKeyCaptured(QKeyEvent* event) const; bool isKeyCaptured(const KeyEvent& event) const; - bool isMouseCaptured() const { return _mouseCaptured; } - bool isTouchCaptured() const { return _touchCaptured; } - bool isWheelCaptured() const { return _wheelCaptured; } - bool areActionsCaptured() const { return _actionsCaptured; } bool isJoystickCaptured(int joystickIndex) const; virtual void update() override; @@ -121,18 +92,6 @@ public slots: virtual void captureKeyEvents(const KeyEvent& event); virtual void releaseKeyEvents(const KeyEvent& event); - virtual void captureMouseEvents() { _mouseCaptured = true; } - virtual void releaseMouseEvents() { _mouseCaptured = false; } - - virtual void captureTouchEvents() { _touchCaptured = true; } - virtual void releaseTouchEvents() { _touchCaptured = false; } - - virtual void captureWheelEvents() { _wheelCaptured = true; } - virtual void releaseWheelEvents() { _wheelCaptured = false; } - - virtual void captureActionEvents() { _actionsCaptured = true; } - virtual void releaseActionEvents() { _actionsCaptured = false; } - virtual void captureJoystick(int joystickIndex); virtual void releaseJoystick(int joystickIndex); @@ -173,10 +132,6 @@ private: int getNumberOfActivePalms() const; const PalmData* getActivePalm(int palmIndex) const; - bool _mouseCaptured; - bool _touchCaptured; - bool _wheelCaptured; - bool _actionsCaptured; QMultiMap _capturedKeys; QSet _capturedJoysticks; diff --git a/libraries/controllers/src/controllers/DeviceProxy.cpp b/libraries/controllers/src/controllers/DeviceProxy.cpp new file mode 100644 index 0000000000..9ac701e80d --- /dev/null +++ b/libraries/controllers/src/controllers/DeviceProxy.cpp @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis on 2015/10/18 +// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) +// 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 +// + +#include "DeviceProxy.h" + +namespace controller { + + float DeviceProxy::getValue(const Input& input, int timestamp) const { + switch (input.getType()) { + case ChannelType::BUTTON: + return getButton(input, timestamp) ? 1.0f : 0.0f; + + case ChannelType::AXIS: + return getAxis(input, timestamp); + + case ChannelType::POSE: + return getPose(input, timestamp)._valid ? 1.0f : 0.0f; + + default: + return NAN; + } + } + +} + diff --git a/libraries/controllers/src/controllers/DeviceProxy.h b/libraries/controllers/src/controllers/DeviceProxy.h new file mode 100644 index 0000000000..78d13fe5c4 --- /dev/null +++ b/libraries/controllers/src/controllers/DeviceProxy.h @@ -0,0 +1,57 @@ +// +// Created by Bradley Austin Davis on 2015/10/18 +// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) +// 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_DeviceProxy_h +#define hifi_controllers_DeviceProxy_h + +#include + +#include +#include + +#include "Input.h" +#include "Pose.h" + +namespace controller { + + using Modifiers = std::vector; + typedef QPair InputPair; + + + template + using InputGetter = std::function; + using ButtonGetter = InputGetter; + using AxisGetter = InputGetter; + using PoseGetter = InputGetter; + using ResetBindings = std::function; + using AvailableInputGetter = std::function; + + class DeviceProxy { + public: + DeviceProxy(QString name) : _baseName(name), _name(name) {} + const QString& getBaseName() const { return _baseName; } + const QString& getName() const { return _name; } + + ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; + AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; + PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); }; + AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector { return Input::NamedVector(); }; + ResetBindings resetDeviceBindings = [] () -> bool { return true; }; + float getValue(const Input& input, int timestamp = 0) const; + + using Pointer = std::shared_ptr; + + QString _baseName; + QString _name; + }; + +} + +#endif diff --git a/libraries/controllers/src/controllers/Input.cpp b/libraries/controllers/src/controllers/Input.cpp new file mode 100644 index 0000000000..29d2fed617 --- /dev/null +++ b/libraries/controllers/src/controllers/Input.cpp @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis on 2015/10/18 +// 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 +// + +#include "Input.h" + +namespace controller { + + const Input Input::INVALID_INPUT = Input(UINT32_MAX); + const uint16_t Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); + const uint16_t Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); + const uint16_t Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); + +} + diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h new file mode 100644 index 0000000000..8cc682df70 --- /dev/null +++ b/libraries/controllers/src/controllers/Input.h @@ -0,0 +1,76 @@ +// +// Created by Bradley Austin Davis on 2015/10/18 +// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) +// 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_Input_h +#define hifi_controllers_Input_h + +#include + +namespace controller { + +enum class ChannelType { + UNKNOWN = 0, + BUTTON = 1, + AXIS, + POSE, +}; + +// Input is the unique identifier to find a n input channel of a particular device +// Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped +// to the Action channels +struct Input { + union { + 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 _padding : 1; // 2 bits to store the Type directly in the ID + }; + uint32_t _id = 0; // by default Input is 0 meaning invalid + }; + + bool isValid() const { return (_id != 0); } + + uint16_t getDevice() const { return _device; } + uint16_t getChannel() const { return _channel; } + uint32_t getID() const { return _id; } + ChannelType getType() const { return (ChannelType) _type; } + + void setDevice(uint16_t device) { _device = device; } + void setChannel(uint16_t channel) { _channel = channel; } + void setType(uint16_t type) { _type = type; } + void setID(uint32_t ID) { _id = ID; } + + bool isButton() const { return getType() == ChannelType::BUTTON; } + bool isAxis() const { return getType() == ChannelType::AXIS; } + bool isPose() const { return getType() == ChannelType::POSE; } + + // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2) + // where the default initializer (a C++-11ism) for the union data above is not applied. + explicit Input() : _id(0) {} + explicit Input(uint32_t id) : _id(id) {} + explicit Input(uint16_t device, uint16_t channel, ChannelType type) : _device(device), _channel(channel), _type(uint16_t(type)), _padding(0) {} + Input(const Input& src) : _id(src._id) {} + Input& operator = (const Input& src) { _id = src._id; return (*this); } + bool operator ==(const Input& right) const { return _id == right._id; } + bool operator < (const Input& src) const { return _id < src._id; } + + static const Input INVALID_INPUT; + static const uint16_t INVALID_DEVICE; + static const uint16_t INVALID_CHANNEL; + static const uint16_t INVALID_TYPE; + + using NamedPair = QPair; + using NamedVector = QVector; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index 4b2376d32a..1f86741b88 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -13,7 +13,7 @@ bool InputDevice::_lowVelocityFilter = false; const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f; -float InputDevice::reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; +float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; //Constants for getCursorPixelRangeMultiplier() const float MIN_PIXEL_RANGE_MULT = 0.4f; @@ -23,7 +23,7 @@ const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; //Returns a multiplier to be applied to the cursor range for the controllers float InputDevice::getCursorPixelRangeMult() { //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) - return InputDevice::reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; + return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; } float InputDevice::getButton(int channel) const { diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 66f7addc58..4854df0ada 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -34,7 +34,7 @@ public: UserInputMapper::PoseValue getPose(int channel) const; virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) {}; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0; // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas @@ -45,8 +45,8 @@ public: int getDeviceID() { return _deviceID; } static float getCursorPixelRangeMult(); - static float getReticleMoveSpeed() { return reticleMoveSpeed; } - static void setReticleMoveSpeed(float sixenseReticleMoveSpeed) { reticleMoveSpeed = sixenseReticleMoveSpeed; } + static float getReticleMoveSpeed() { return _reticleMoveSpeed; } + static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; } static bool getLowVelocityFilter() { return _lowVelocityFilter; }; @@ -72,5 +72,5 @@ protected: static bool _lowVelocityFilter; private: - static float reticleMoveSpeed; + static float _reticleMoveSpeed; }; \ No newline at end of file diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp new file mode 100644 index 0000000000..a716955d70 --- /dev/null +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis on 2015/10/18 +// 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 +// + +#include "Pose.h" + +namespace controller { + + Pose::Pose(const vec3& translation, const quat& rotation, + const vec3& velocity, const quat& angularVelocity) : + _translation(translation), _rotation(rotation), _velocity(velocity), _angularVelocity(angularVelocity) { } + + bool Pose::operator==(const Pose& right) const { + // invalid poses return false for comparison, even against identical invalid poses, like NaN + if (_valid || !right._valid) { + return false; + } + + // FIXME add margin of error? Or add an additional withinEpsilon function? + return _translation == right.getTranslation() && _rotation == right.getRotation() && + _velocity == right.getVelocity() && _angularVelocity == right.getAngularVelocity(); + } + + +} + diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h new file mode 100644 index 0000000000..b77064f2c1 --- /dev/null +++ b/libraries/controllers/src/controllers/Pose.h @@ -0,0 +1,43 @@ +// +// Created by Bradley Austin Davis on 2015/10/18 +// (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) +// 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_Pose_h +#define hifi_controllers_Pose_h + +#include + +namespace controller { + + struct Pose { + public: + vec3 _translation; + quat _rotation; + vec3 _velocity; + quat _angularVelocity; + bool _valid{ false }; + + Pose() {} + Pose(const vec3& translation, const quat& rotation, + const vec3& velocity = vec3(), const quat& angularVelocity = quat()); + + Pose(const Pose&) = default; + Pose& operator = (const Pose&) = default; + bool operator ==(const Pose& right) const; + bool isValid() const { return _valid; } + vec3 getTranslation() const { return _translation; } + quat getRotation() const { return _rotation; } + vec3 getVelocity() const { return _velocity; } + quat getAngularVelocity() const { return _angularVelocity; } + }; + + +} + +#endif diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 6d4ad1a566..c5c7969604 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -67,7 +67,7 @@ namespace controller { void ScriptEndpoint::updateValue() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); return; } @@ -80,10 +80,10 @@ namespace controller { void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, - Q_ARG(float, newValue), - Q_ARG(float, oldValue), - Q_ARG(int, sourceID)); + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(float, newValue), + Q_ARG(float, oldValue), + Q_ARG(int, sourceID)); return; } _callable.call(QScriptValue(), @@ -163,28 +163,28 @@ namespace controller { QJsonObject obj; QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error); - // check validity of the document - if (!doc.isNull()) { - if (doc.isObject()) { - obj = doc.object(); - + // check validity of the document + if (!doc.isNull()) { + if (doc.isObject()) { + obj = doc.object(); + auto mapping = std::make_shared("default"); auto mappingBuilder = new MappingBuilderProxy(*this, mapping); mappingBuilder->parse(obj); _mappingsByName[mapping->_name] = mapping; - - return mappingBuilder; - } else { - qDebug() << "Mapping json Document is not an object" << endl; - } - } else { - qDebug() << "Invalid JSON...\n"; - qDebug() << error.errorString(); - qDebug() << "JSON was:\n" << json << endl; - - } + + return mappingBuilder; + } else { + qDebug() << "Mapping json Document is not an object" << endl; + } + } else { + qDebug() << "Invalid JSON...\n"; + qDebug() << error.errorString(); + qDebug() << "JSON was:\n" << json << endl; + + } return nullptr; } @@ -204,7 +204,7 @@ namespace controller { if (request->getResult() == ResourceRequest::Success) { result = parseMapping(QString(request->getData())); } else { - qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl; + qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl; } request->deleteLater(); } @@ -263,8 +263,8 @@ namespace controller { return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::AXIS).getID()); } - glm::mat4 ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { - return glm::mat4(); + Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { + return Pose(); } void ScriptingInterface::update() { @@ -368,7 +368,7 @@ namespace controller { } UserInputMapper::Input ScriptingInterface::inputFor(const QString& inputName) { - return DependencyManager::get()->findDeviceInput(inputName); + return DependencyManager::get()->findDeviceInput(inputName); } Endpoint::Pointer ScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { @@ -494,6 +494,39 @@ namespace controller { } } } + + QVector ScriptingInterface::getAllActions() { + return DependencyManager::get()->getAllActions(); + } + + QString ScriptingInterface::getDeviceName(unsigned int device) { + return DependencyManager::get()->getDeviceName((unsigned short)device); + } + + QVector ScriptingInterface::getAvailableInputs(unsigned int device) { + return DependencyManager::get()->getAvailableInputs((unsigned short)device); + } + + int ScriptingInterface::findDevice(QString name) { + return DependencyManager::get()->findDevice(name); + } + + QVector ScriptingInterface::getDeviceNames() { + return DependencyManager::get()->getDeviceNames(); + } + + float ScriptingInterface::getActionValue(int action) { + return DependencyManager::get()->getActionState(UserInputMapper::Action(action)); + } + + int ScriptingInterface::findAction(QString actionName) { + return DependencyManager::get()->findAction(actionName); + } + + QVector ScriptingInterface::getActionNames() const { + return DependencyManager::get()->getActionNames(); + } + } // namespace controllers @@ -520,7 +553,7 @@ ScriptingInterface::ScriptingInterface() { int actionNumber = 0; qCDebug(controllers) << "Setting up standard actions"; for (const auto& actionName : actionNames) { - UserInputMapper::Input actionInput(UserInputMapper::Input::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); + UserInputMapper::Input actionInput(UserInputMapper::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); // Expose the IDs to JS QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); @@ -532,43 +565,3 @@ ScriptingInterface::ScriptingInterface() { updateMaps(); } - -//var mapping = Controller.newMapping(); -//mapping.map(hydra.LeftButton0, actions.ContextMenu); -//mapping.map(hydra.LeftButton0).to(xbox.RT); -//mapping.from(xbox.RT).constrainToBoolean().invert().to(actions.Foo) -// mapping.from(xbox.RY).invert().deadZone(0.2).to(actions.Pitch) -// mapping.from(xbox.RY).filter(function(newValue, oldValue) { -// return newValue * 2.0 -//}).to(actions.Pitch) - -//mapping.from(function(time) { -// return Math.cos(time); -// }).to(actions.Pitch); - -// mapping.mapFromFunction(function() { -// return x; -// }, actions.ContextMenu); - -// mapping.from(xbox.LY).clamp(0, 1).to(actions.Forward); -// mapping.from(xbox.LY).clamp(-1, 0).to(actions.Backward); -// mapping.from(xbox.RY).clamp(0, 1).to(actions.Forward); -// mapping.from(xbox.RS).to(); -// mapping.from(xbox.ALL).to(); - -// mapping.from(xbox.RY).to(function(...) { ... }); -// mapping.from(xbox.RY).pass(); - -// mapping.suppress() ≅ mapping.to(null) -// mapping.pass() ≅ mapping.to(fromControl) - -// mapping.from(keyboard.RightParen).invert().to(actions.Yaw) -// mapping.from(keyboard.LeftParen).to(actions.Yaw) - -// mapping.from(hydra.LX).pulse(MIN_SNAP_TIME, 3.0).to(Actions.Yaw) - -// mapping.from(keyboard.LeftParen).pulse(MIN_SNAP_TIME).to(Actions.Yaw) -// // Enable and disable as above - -// mappingSnap.from(hydra.LX).to(function(newValue, oldValue) { -// timeSinceLastYaw += deltaTime diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index ef9d61c32d..f473562b9e 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -67,15 +67,22 @@ namespace controller { ScriptingInterface(); virtual ~ScriptingInterface(); + Q_INVOKABLE QVector getAllActions(); + Q_INVOKABLE QVector getAvailableInputs(unsigned int device); + Q_INVOKABLE QString getDeviceName(unsigned int device); + Q_INVOKABLE float getActionValue(int action); + Q_INVOKABLE int findDevice(QString name); + Q_INVOKABLE QVector getDeviceNames(); + Q_INVOKABLE int findAction(QString actionName); + Q_INVOKABLE QVector getActionNames() const; + Q_INVOKABLE float getValue(const int& source) const; Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const; Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const; - Q_INVOKABLE glm::mat4 getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; + Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); - Q_INVOKABLE void disableMapping(const QString& mappingName) { - enableMapping(mappingName, false); - } + Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); } Q_INVOKABLE QObject* parseMapping(const QString& json); Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); @@ -102,13 +109,30 @@ namespace controller { Q_INVOKABLE const QVariantMap& getActions() { return _actions; } Q_INVOKABLE const QVariantMap& getStandard() { return _standard; } + bool isMouseCaptured() const { return _mouseCaptured; } + bool isTouchCaptured() const { return _touchCaptured; } + bool isWheelCaptured() const { return _wheelCaptured; } + bool areActionsCaptured() const { return _actionsCaptured; } + static QRegularExpression SANITIZE_NAME_EXPRESSION; public slots: virtual void update(); - virtual void registerControllerTypes(QScriptEngine* engine) = 0; virtual void updateMaps(); + virtual void captureMouseEvents() { _mouseCaptured = true; } + virtual void releaseMouseEvents() { _mouseCaptured = false; } + + virtual void captureTouchEvents() { _touchCaptured = true; } + virtual void releaseTouchEvents() { _touchCaptured = false; } + + virtual void captureWheelEvents() { _wheelCaptured = true; } + virtual void releaseWheelEvents() { _wheelCaptured = false; } + + virtual void captureActionEvents() { _actionsCaptured = true; } + virtual void releaseActionEvents() { _actionsCaptured = false; } + + private: friend class MappingBuilderProxy; friend class RouteBuilderProxy; @@ -141,6 +165,11 @@ namespace controller { ValueMap _overrideValues; MappingMap _mappingsByName; MappingStack _activeMappings; + + bool _mouseCaptured{ false }; + bool _touchCaptured{ false }; + bool _wheelCaptured{ false }; + bool _actionsCaptured{ false }; }; class ScriptEndpoint : public Endpoint { diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 9c423eded2..6b1ada25ed 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -96,16 +96,65 @@ void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { return availableInputs; }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerStandardDevice(proxy); } void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float DPAD_MOVE_SPEED = 0.5f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BOOM_SPEED = 0.1f; + + // Y axes are flipped (up is negative) + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED); + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LB), BOOM_SPEED); + + // Hold front right shoulder button for precision controls + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); + + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); } UserInputMapper::Input StandardController::makeInput(controller::StandardButtonChannel button) { diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index fbf1994d87..2c80eb98a1 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -1,7 +1,4 @@ // -// UserInputMapper.cpp -// input-plugins/src/input-plugins -// // Created by Sam Gateau on 4/27/15. // Copyright 2015 High Fidelity, Inc. // @@ -12,14 +9,10 @@ #include "UserInputMapper.h" #include "StandardController.h" -#include "Logging.h" +#include "Logging.h" -const UserInputMapper::Input UserInputMapper::Input::INVALID_INPUT = UserInputMapper::Input(UINT16_MAX); -const uint16_t UserInputMapper::Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); -const uint16_t UserInputMapper::Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); -const uint16_t UserInputMapper::Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); -const uint16_t UserInputMapper::Input::ACTIONS_DEVICE = INVALID_DEVICE - (uint16)1; -const uint16_t UserInputMapper::Input::STANDARD_DEVICE = 0; +const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - (uint16)1; +const uint16_t UserInputMapper::STANDARD_DEVICE = 0; // Default contruct allocate the poutput size with the current hardcoded action channels UserInputMapper::UserInputMapper() { @@ -46,18 +39,18 @@ bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer proxy->_name += QString::number(numberOfType); } - qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; + qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; _registeredDevices[deviceID] = proxy; return true; } -bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { - device->_name = "Standard"; // Just to make sure - _registeredDevices[getStandardDeviceID()] = device; - return true; -} +bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { + device->_name = "Standard"; // Just to make sure + _registeredDevices[getStandardDeviceID()] = device; + return true; +} UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { @@ -110,51 +103,51 @@ QVector UserInputMapper::getDeviceNames() { return result; } -UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { - - // Split the full input name as such: deviceName.inputName - auto names = inputName.split('.'); - - if (names.size() >= 2) { - // Get the device name: - auto deviceName = names[0]; - auto inputName = names[1]; - - int deviceID = findDevice(deviceName); - if (deviceID != Input::INVALID_DEVICE) { - const auto& deviceProxy = _registeredDevices.at(deviceID); - auto deviceInputs = deviceProxy->getAvailabeInputs(); - - for (auto input : deviceInputs) { - if (input.second == inputName) { - return input.first; - } - } - - qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; - - } else if (deviceName == "Actions") { - deviceID = Input::ACTIONS_DEVICE; - int actionNum = 0; - for (auto action : _actionNames) { - if (action == inputName) { - return Input(Input::ACTIONS_DEVICE, actionNum, ChannelType::AXIS); - } - actionNum++; - } - - qCDebug(controllers) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions"; - - } else { - qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">"; - } - } else { - qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; - } - - return Input(); -} - +UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { + + // Split the full input name as such: deviceName.inputName + auto names = inputName.split('.'); + + if (names.size() >= 2) { + // Get the device name: + auto deviceName = names[0]; + auto inputName = names[1]; + + int deviceID = findDevice(deviceName); + if (deviceID != Input::INVALID_DEVICE) { + const auto& deviceProxy = _registeredDevices.at(deviceID); + auto deviceInputs = deviceProxy->getAvailabeInputs(); + + for (auto input : deviceInputs) { + if (input.second == inputName) { + return input.first; + } + } + + qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; + + } else if (deviceName == "Actions") { + deviceID = ACTIONS_DEVICE; + int actionNum = 0; + for (auto action : _actionNames) { + if (action == inputName) { + return Input(ACTIONS_DEVICE, actionNum, ChannelType::AXIS); + } + actionNum++; + } + + qCDebug(controllers) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions"; + + } else { + qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">"; + } + } else { + qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; + } + + return Input::INVALID_INPUT; +} + bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { @@ -261,7 +254,6 @@ void UserInputMapper::update(float deltaTime) { } int currentTimestamp = 0; - for (auto& channelInput : _actionToInputsMap) { auto& inputMapping = channelInput.second; auto& inputID = inputMapping._input; @@ -430,18 +422,62 @@ void UserInputMapper::registerStandardDevice() { _standardController->registerToUserInputMapper(*this); } -float UserInputMapper::DeviceProxy::getValue(const Input& input, int timestamp) const { - switch (input.getType()) { - case UserInputMapper::ChannelType::BUTTON: - return getButton(input, timestamp) ? 1.0f : 0.0f; - case UserInputMapper::ChannelType::AXIS: - return getAxis(input, timestamp); +static int actionMetaTypeId = qRegisterMetaType(); +static int inputMetaTypeId = qRegisterMetaType(); +static int inputPairMetaTypeId = qRegisterMetaType(); - case UserInputMapper::ChannelType::POSE: - return getPose(input, timestamp)._valid ? 1.0f : 0.0f; +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); - default: - return 0.0f; - } -} \ No newline at end of file +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { + QScriptValue obj = engine->newObject(); + obj.setProperty("device", input.getDevice()); + obj.setProperty("channel", input.getChannel()); + obj.setProperty("type", (unsigned short)input.getType()); + obj.setProperty("id", input.getID()); + return obj; +} + +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { + input.setDevice(object.property("device").toUInt16()); + input.setChannel(object.property("channel").toUInt16()); + input.setType(object.property("type").toUInt16()); + input.setID(object.property("id").toInt32()); +} + +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { + QScriptValue obj = engine->newObject(); + auto userInputMapper = DependencyManager::get(); + obj.setProperty("action", (int)action); + obj.setProperty("actionName", userInputMapper->getActionName(action)); + return obj; +} + +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { + action = UserInputMapper::Action(object.property("action").toVariant().toInt()); +} + +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { + QScriptValue obj = engine->newObject(); + obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); + obj.setProperty("inputName", inputPair.second); + return obj; +} + +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { + inputFromScriptValue(object.property("input"), inputPair.first); + inputPair.second = QString(object.property("inputName").toVariant().toString()); +} + +void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); + qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); + qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); +} diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 8b466d79c9..b8bc0850cf 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -1,7 +1,4 @@ // -// UserInputMapper.h -// input-plugins/src/input-plugins -// // Created by Sam Gateau on 4/27/15. // Copyright 2015 High Fidelity, Inc. // @@ -9,6 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#pragma once #ifndef hifi_UserInputMapper_h #define hifi_UserInputMapper_h @@ -20,6 +18,10 @@ #include #include +#include "Pose.h" +#include "Input.h" +#include "DeviceProxy.h" + class StandardController; typedef std::shared_ptr StandardControllerPointer; @@ -30,86 +32,22 @@ class UserInputMapper : public QObject, public Dependency { public: ~UserInputMapper(); + using DeviceProxy = controller::DeviceProxy; + using PoseValue = controller::Pose; + using Input = controller::Input; + using ChannelType = controller::ChannelType; + typedef unsigned short uint16; typedef unsigned int uint32; - enum class ChannelType { - UNKNOWN = 0, - BUTTON = 1, - AXIS, - POSE, - }; + static void registerControllerTypes(QScriptEngine* engine); - // Input is the unique identifier to find a n input channel of a particular device - // Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped - // to the Action channels - class Input { - public: - union { - struct { - uint16 _device; // Up to 64K possible devices - uint16 _channel : 13; // 2^13 possible channel per Device - uint16 _type : 2; // 2 bits to store the Type directly in the ID - uint16 _padding : 1; // 2 bits to store the Type directly in the ID - }; - uint32 _id = 0; // by default Input is 0 meaning invalid - }; - - bool isValid() const { return (_id != 0); } - - uint16 getDevice() const { return _device; } - uint16 getChannel() const { return _channel; } - uint32 getID() const { return _id; } - ChannelType getType() const { return (ChannelType) _type; } - - void setDevice(uint16 device) { _device = device; } - void setChannel(uint16 channel) { _channel = channel; } - void setType(uint16 type) { _type = type; } - void setID(uint32 ID) { _id = ID; } - - bool isButton() const { return getType() == ChannelType::BUTTON; } - bool isAxis() const { return getType() == ChannelType::AXIS; } - bool isPose() const { return getType() == ChannelType::POSE; } - - // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2) - // where the default initializer (a C++-11ism) for the union data above is not applied. - explicit Input() : _id(0) {} - explicit Input(uint32 id) : _id(id) {} - explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)), _padding(0) {} - Input(const Input& src) : _id(src._id) {} - Input& operator = (const Input& src) { _id = src._id; return (*this); } - bool operator ==(const Input& right) const { return _id == right._id; } - bool operator < (const Input& src) const { return _id < src._id; } - - static const Input INVALID_INPUT; - static const uint16 INVALID_DEVICE; - static const uint16 INVALID_CHANNEL; - static const uint16 INVALID_TYPE; - static const uint16 ACTIONS_DEVICE; - static const uint16 STANDARD_DEVICE; - }; + static const uint16 ACTIONS_DEVICE; + static const uint16 STANDARD_DEVICE; // Modifiers are just button inputID typedef std::vector< Input > Modifiers; - - class PoseValue { - public: - glm::vec3 _translation{ 0.0f }; - glm::quat _rotation; - bool _valid; - - PoseValue() : _valid(false) {}; - PoseValue(glm::vec3 translation, glm::quat rotation) : _translation(translation), _rotation(rotation), _valid(true) {} - PoseValue(const PoseValue&) = default; - PoseValue& operator = (const PoseValue&) = default; - bool operator ==(const PoseValue& right) const { return _translation == right.getTranslation() && _rotation == right.getRotation() && _valid == right.isValid(); } - - bool isValid() const { return _valid; } - glm::vec3 getTranslation() const { return _translation; } - glm::quat getRotation() const { return _rotation; } - }; - typedef std::function ButtonGetter; typedef std::function AxisGetter; typedef std::function PoseGetter; @@ -119,22 +57,6 @@ public: typedef QVector AvailableInput; - class DeviceProxy { - public: - DeviceProxy(QString name) : _baseName(name), _name(name) {} - const QString& getBaseName() const { return _baseName; } - const QString& getName() const { return _name; } - - QString _baseName; - QString _name; - ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; - AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; - PoseGetter getPose = [] (const Input& input, int timestamp) -> PoseValue { return PoseValue(); }; - AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector(); }; - ResetBindings resetDeviceBindings = [] () -> bool { return true; }; - float getValue(const Input& input, int timestamp = 0) const; - typedef std::shared_ptr Pointer; - }; // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } @@ -222,7 +144,7 @@ public: // Return true if theinput channel is created correctly, false either bool addInputChannel(Action action, const Input& input, float scale = 1.0f); bool addInputChannel(Action action, const Input& input, const Input& modifer, float scale = 1.0f); - + // Under the hood, the input channels are organized in map sorted on the _output // The InputChannel class is just the full values describing the input channel in one object class InputChannel { @@ -276,7 +198,7 @@ public: typedef std::map DevicesMap; DevicesMap getDevices() { return _registeredDevices; } - uint16 getStandardDeviceID() const { return Input::STANDARD_DEVICE; } + uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } signals: @@ -288,7 +210,7 @@ protected: StandardControllerPointer _standardController; DevicesMap _registeredDevices; - uint16 _nextFreeDeviceID = Input::STANDARD_DEVICE + 1; + uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; typedef std::map InputToMoModifiersMap; InputToMoModifiersMap _inputToModifiersMap; @@ -309,6 +231,7 @@ protected: }; Q_DECLARE_METATYPE(UserInputMapper::InputPair) +Q_DECLARE_METATYPE(UserInputMapper::PoseValue) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(UserInputMapper::Input) Q_DECLARE_METATYPE(UserInputMapper::InputChannel) diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index aa5bbbba07..09e81f7346 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -143,3 +143,63 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { mapper.registerDevice(_deviceID, proxy); } + +void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { +#ifdef HAVE_SDL2 + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float DPAD_MOVE_SPEED = 0.5f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BOOM_SPEED = 0.1f; + + // Y axes are flipped (up is negative) + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED); + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), BOOM_SPEED); + + // Hold front right shoulder button for precision controls + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); + + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); +#endif +} \ No newline at end of file diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index 8e4cdb365f..ad99774737 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -37,6 +37,7 @@ public: // Device functions virtual void registerToUserInputMapper(UserInputMapper& mapper) override; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 9d3d06ecb7..e8cebd8e54 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -449,6 +449,17 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, boo #endif // HAVE_SIXENSE } +static const auto L0 = controller::BACK; +static const auto L1 = controller::DL; +static const auto L2 = controller::DD; +static const auto L3 = controller::DR; +static const auto L4 = controller::DU; +static const auto R0 = controller::START; +static const auto R1 = controller::X; +static const auto R2 = controller::A; +static const auto R3 = controller::B; +static const auto R4 = controller::Y; + void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { // Grab the current free device ID _deviceID = mapper.getFreeDeviceID(); @@ -459,31 +470,75 @@ void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { using namespace controller; proxy->getAvailabeInputs = [this]() -> QVector { QVector availableInputs; - availableInputs.append(UserInputMapper::InputPair(makeInput(BACK), "L0")); - availableInputs.append(UserInputMapper::InputPair(makeInput(DL), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(DD), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(DR), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(DU), "L4")); + availableInputs.append(UserInputMapper::InputPair(makeInput(L0), "L0")); + availableInputs.append(UserInputMapper::InputPair(makeInput(L1), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(L2), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(L3), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(L4), "L4")); availableInputs.append(UserInputMapper::InputPair(makeInput(LB), "LB")); availableInputs.append(UserInputMapper::InputPair(makeInput(LS), "LS")); availableInputs.append(UserInputMapper::InputPair(makeInput(LX), "LX")); availableInputs.append(UserInputMapper::InputPair(makeInput(LY), "LY")); availableInputs.append(UserInputMapper::InputPair(makeInput(LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(START), "R0")); - availableInputs.append(UserInputMapper::InputPair(makeInput(X), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(A), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(B), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Y), "R4")); + availableInputs.append(UserInputMapper::InputPair(makeInput(R0), "R0")); + availableInputs.append(UserInputMapper::InputPair(makeInput(R1), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(R2), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(R3), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(R4), "R4")); availableInputs.append(UserInputMapper::InputPair(makeInput(RB), "RB")); availableInputs.append(UserInputMapper::InputPair(makeInput(RS), "RS")); availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX")); availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY")); availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT), "LeftPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT), "RightPose")); return availableInputs; }; mapper.registerDevice(_deviceID, proxy); } +void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BUTTON_MOVE_SPEED = 1.0f; + const float BOOM_SPEED = 0.1f; + using namespace controller; + + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(LY), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(LX), JOYSTICK_MOVE_SPEED); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(RX), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(RY), JOYSTICK_PITCH_SPEED); + + // Buttons + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(L3), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(L1), BOOM_SPEED); + + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(R3), BUTTON_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(R1), BUTTON_MOVE_SPEED); + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(L2)); + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(R2)); + + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(L4)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(R4)); + + // FIXME +// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); +// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); + + mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(LT)); + mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(RT)); + + // TODO find a mechanism to allow users to navigate the context menu via + mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(L0)); + mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(R0)); + +} + // virtual void SixenseManager::saveSettings() const { Settings settings; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 2e7dd3223d..897ca72940 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -61,6 +61,8 @@ public: // Device functions virtual void registerToUserInputMapper(UserInputMapper& mapper) override; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0dfc2fe09e..dc08fd7a69 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -308,8 +308,6 @@ void ScriptEngine::init() { registerGlobalObject("Script", this); registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); - auto scriptingInterface = DependencyManager::get(); - registerGlobalObject("Controller", scriptingInterface.data()); registerGlobalObject("Entities", entityScriptingInterface.data()); registerGlobalObject("Quat", &_quatLibrary); registerGlobalObject("Vec3", &_vec3Library); @@ -319,11 +317,9 @@ void ScriptEngine::init() { // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); - if (scriptingInterface) { - scriptingInterface->registerControllerTypes(this); - } - - + auto scriptingInterface = DependencyManager::get(); + registerGlobalObject("Controller", scriptingInterface.data()); + UserInputMapper::registerControllerTypes(this); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From 184303d3c9a817593052ec64bcc1e04fb8a2a267 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 18 Oct 2015 20:44:32 -0700 Subject: [PATCH 059/232] Using JS mappings for joystick, updating test code --- interface/resources/qml/TestControllers.qml | 57 +++++++++++++++++-- .../src/controllers/UserInputMapper.cpp | 35 +++++++++++- .../src/controllers/UserInputMapper.h | 8 ++- .../src/input-plugins/Joystick.cpp | 2 + 4 files changed, 93 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index d8c9cb4343..137a8548ac 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -50,9 +50,38 @@ HifiControls.VrDialog { Row { spacing: 8 Button { - text: "Default Mapping" + text: "Standard Mapping" onClicked: { var mapping = Controller.newMapping("Default"); + mapping.from(standard.LX).to(actions.TranslateX); + mapping.from(standard.LY).to(actions.TranslateZ); + mapping.from(standard.RY).to(actions.Pitch); + mapping.from(standard.RX).to(actions.Yaw); + mapping.from(standard.DU).scale(0.5).to(actions.LONGITUDINAL_FORWARD); + mapping.from(standard.DD).scale(0.5).to(actions.LONGITUDINAL_BACKWARD); + mapping.from(standard.DL).scale(0.5).to(actions.LATERAL_LEFT); + mapping.from(standard.DR).scale(0.5).to(actions.LATERAL_RIGHT); + mapping.from(standard.X).to(actions.VERTICAL_DOWN); + mapping.from(standard.Y).to(actions.VERTICAL_UP); + mapping.from(standard.RT).scale(0.1).to(actions.BOOM_IN); + mapping.from(standard.LT).scale(0.1).to(actions.BOOM_OUT); + mapping.from(standard.B).to(actions.ACTION1); + mapping.from(standard.A).to(actions.ACTION2); + mapping.from(standard.RB).to(actions.SHIFT); + mapping.from(standard.Back).to(actions.TOGGLE_MUTE); + mapping.from(standard.Start).to(actions.CONTEXT_MENU); + Controller.enableMapping("Default"); + enabled = false; + text = "Standard Built" + } + } + + Button { + text: root.xbox ? "XBox Mapping" : "XBox not found" + property bool built: false + enabled: root.xbox && !built + onClicked: { + var mapping = Controller.newMapping(); mapping.from(xbox.A).to(standard.A); mapping.from(xbox.B).to(standard.B); mapping.from(xbox.X).to(standard.X); @@ -73,14 +102,32 @@ HifiControls.VrDialog { mapping.from(xbox.RX).to(standard.RX); mapping.from(xbox.LT).to(standard.LT); mapping.from(xbox.RT).to(standard.RT); - Controller.enableMapping("Default"); - enabled = false; - text = "Built" + mapping.enable(); + built = false; + text = "XBox Built" } } Button { - text: "Build Mapping" + text: root.hydra ? "Hydra Mapping" : "Hydra Not Found" + property bool built: false + enabled: root.hydra && !built + onClicked: { + var mapping = Controller.newMapping(); + mapping.from(hydra.LY).invert().to(standard.LY); + mapping.from(hydra.LX).to(standard.LX); + mapping.from(hydra.RY).invert().to(standard.RY); + mapping.from(hydra.RX).to(standard.RX); + mapping.from(hydra.LT).to(standard.LT); + mapping.from(hydra.RT).to(standard.RT); + mapping.enable(); + built = false; + text = "Hydra Built" + } + } + + Button { + text: "Test Mapping" onClicked: { var mapping = Controller.newMapping(); // Inverting a value diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 2c80eb98a1..672d6a2542 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -242,6 +242,12 @@ QVector UserInputMapper::getAllInputsForDevice(ui return channels; } +void fixBisectedAxis(float& full, float& negative, float& positive) { + full = full + (negative * -1.0f) + positive; + negative = full >= 0.0f ? 0.0f : full * -1.0f; + positive = full <= 0.0f ? 0.0f : full; +} + void UserInputMapper::update(float deltaTime) { // Reset the axis state for next loop @@ -303,14 +309,25 @@ void UserInputMapper::update(float deltaTime) { } // Scale all the channel step with the scale - static const float EPSILON = 0.01f; for (auto i = 0; i < NUM_ACTIONS; i++) { if (_externalActionStates[i] != 0) { _actionStates[i] += _externalActionStates[i]; _externalActionStates[i] = 0.0f; } - _actionStates[i] *= _actionScales[i]; + } + // merge the bisected and non-bisected axes for now + fixBisectedAxis(_actionStates[TRANSLATE_X], _actionStates[LATERAL_LEFT], _actionStates[LATERAL_RIGHT]); + fixBisectedAxis(_actionStates[TRANSLATE_Y], _actionStates[VERTICAL_DOWN], _actionStates[VERTICAL_UP]); + fixBisectedAxis(_actionStates[TRANSLATE_Z], _actionStates[LONGITUDINAL_FORWARD], _actionStates[LONGITUDINAL_BACKWARD]); + fixBisectedAxis(_actionStates[TRANSLATE_CAMERA_Z], _actionStates[BOOM_IN], _actionStates[BOOM_OUT]); + fixBisectedAxis(_actionStates[ROTATE_Y], _actionStates[YAW_LEFT], _actionStates[YAW_RIGHT]); + fixBisectedAxis(_actionStates[ROTATE_X], _actionStates[PITCH_UP], _actionStates[PITCH_DOWN]); + + + static const float EPSILON = 0.01f; + for (auto i = 0; i < NUM_ACTIONS; i++) { + _actionStates[i] *= _actionScales[i]; // Emit only on change, and emit when moving back to 0 if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { _lastActionStates[i] = _actionStates[i]; @@ -420,9 +437,9 @@ void UserInputMapper::createActionNames() { void UserInputMapper::registerStandardDevice() { _standardController = std::make_shared(); _standardController->registerToUserInputMapper(*this); + _standardController->assignDefaultInputMapping(*this); } - static int actionMetaTypeId = qRegisterMetaType(); static int inputMetaTypeId = qRegisterMetaType(); static int inputPairMetaTypeId = qRegisterMetaType(); @@ -481,3 +498,15 @@ void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); } + +UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { + return Input(STANDARD_DEVICE, button, ChannelType::BUTTON); +} + +UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) { + return Input(STANDARD_DEVICE, axis, ChannelType::AXIS); +} + +UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { + return Input(STANDARD_DEVICE, pose, ChannelType::POSE); +} diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index b8bc0850cf..d463ed0482 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -21,6 +21,7 @@ #include "Pose.h" #include "Input.h" #include "DeviceProxy.h" +#include "StandardControls.h" class StandardController; typedef std::shared_ptr StandardControllerPointer; @@ -84,8 +85,9 @@ public: ROTATE_Z, ROLL = ROTATE_Z, TRANSLATE_CAMERA_Z, + NUM_COMBINED_AXES, - LEFT_HAND, + LEFT_HAND = NUM_COMBINED_AXES, RIGHT_HAND, LEFT_HAND_CLICK, @@ -145,6 +147,10 @@ public: bool addInputChannel(Action action, const Input& input, float scale = 1.0f); bool addInputChannel(Action action, const Input& input, const Input& modifer, float scale = 1.0f); + UserInputMapper::Input makeStandardInput(controller::StandardButtonChannel button); + UserInputMapper::Input makeStandardInput(controller::StandardAxisChannel axis); + UserInputMapper::Input makeStandardInput(controller::StandardPoseChannel pose); + // Under the hood, the input channels are organized in map sorted on the _output // The InputChannel class is just the full values describing the input channel in one object class InputChannel { diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index 09e81f7346..9d9ac8bc26 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -145,6 +145,7 @@ void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { +#if 0 #ifdef HAVE_SDL2 const float JOYSTICK_MOVE_SPEED = 1.0f; const float DPAD_MOVE_SPEED = 0.5f; @@ -202,4 +203,5 @@ void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); #endif +#endif } \ No newline at end of file From a8e707ced26ed5853d0f0c04dbf24e330d42ce10 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 18 Oct 2015 22:46:14 -0700 Subject: [PATCH 060/232] Correcting update order to fix mouse --- interface/src/Application.cpp | 16 ---------------- .../scripting/ControllerScriptingInterface.cpp | 13 ++++++++----- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b0c0109b71..77140fc0d3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2710,22 +2710,6 @@ void Application::update(float deltaTime) { auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); - // userInputMapper->update(deltaTime); - - // This needs to go after userInputMapper->update() because of the keyboard - bool jointsCaptured = false; - auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); - foreach(auto inputPlugin, inputPlugins) { - QString name = inputPlugin->getName(); - QAction* action = Menu::getInstance()->getActionForOption(name); - if (action && action->isChecked()) { - inputPlugin->pluginUpdate(deltaTime, jointsCaptured); - if (inputPlugin->isJointController()) { - jointsCaptured = true; - } - } - } - // Dispatch input events _controllerScriptingInterface->update(); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 931c97a7d2..1e35713e16 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -173,15 +173,18 @@ void ControllerScriptingInterface::update() { float delta = now - last; last = now; - for(auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { + DependencyManager::get()->update(delta); + + bool jointsCaptured = false; + for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { if (inputPlugin->isActive()) { - inputPlugin->pluginUpdate(delta, false); + inputPlugin->pluginUpdate(delta, jointsCaptured); + if (inputPlugin->isJointController()) { + jointsCaptured = true; + } } } - auto userInputMapper = DependencyManager::get(); - userInputMapper->update(delta); - for (auto entry : _inputControllers) { entry.second->update(); } From 22602fb6f6506c5af6f7fa04ca51573c41d393bd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 10:00:16 -0700 Subject: [PATCH 061/232] Fixing line endings --- interface/resources/qml/TestControllers.qml | 436 +++---- .../src/controllers/UserInputMapper.cpp | 1024 ++++++++--------- .../src/input-plugins/Joystick.cpp | 412 +++---- .../src/input-plugins/Joystick.h | 140 +-- 4 files changed, 1006 insertions(+), 1006 deletions(-) diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index 137a8548ac..79be877aa3 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -1,218 +1,218 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -import "controller" -import "controls" as HifiControls -import "styles" - -HifiControls.VrDialog { - id: root - HifiConstants { id: hifi } - title: "Controller Test" - resizable: true - contentImplicitWidth: clientArea.implicitWidth - contentImplicitHeight: clientArea.implicitHeight - backgroundColor: "beige" - - property var actions: Controller.Actions - property var standard: Controller.Standard - property var hydra: null - property var testMapping: null - property var xbox: null - - - Component.onCompleted: { - enabled = true - var xboxRegex = /^X360Controller/; - var hydraRegex = /^Hydra/; - for (var prop in Controller.Hardware) { - if(xboxRegex.test(prop)) { - root.xbox = Controller.Hardware[prop] - print("found xbox") - continue - } - if (hydraRegex.test(prop)) { - root.hydra = Controller.Hardware[prop] - print("found hydra") - continue - } - } - } - - Column { - id: clientArea - spacing: 12 - x: root.clientX - y: root.clientY - - Row { - spacing: 8 - Button { - text: "Standard Mapping" - onClicked: { - var mapping = Controller.newMapping("Default"); - mapping.from(standard.LX).to(actions.TranslateX); - mapping.from(standard.LY).to(actions.TranslateZ); - mapping.from(standard.RY).to(actions.Pitch); - mapping.from(standard.RX).to(actions.Yaw); - mapping.from(standard.DU).scale(0.5).to(actions.LONGITUDINAL_FORWARD); - mapping.from(standard.DD).scale(0.5).to(actions.LONGITUDINAL_BACKWARD); - mapping.from(standard.DL).scale(0.5).to(actions.LATERAL_LEFT); - mapping.from(standard.DR).scale(0.5).to(actions.LATERAL_RIGHT); - mapping.from(standard.X).to(actions.VERTICAL_DOWN); - mapping.from(standard.Y).to(actions.VERTICAL_UP); - mapping.from(standard.RT).scale(0.1).to(actions.BOOM_IN); - mapping.from(standard.LT).scale(0.1).to(actions.BOOM_OUT); - mapping.from(standard.B).to(actions.ACTION1); - mapping.from(standard.A).to(actions.ACTION2); - mapping.from(standard.RB).to(actions.SHIFT); - mapping.from(standard.Back).to(actions.TOGGLE_MUTE); - mapping.from(standard.Start).to(actions.CONTEXT_MENU); - Controller.enableMapping("Default"); - enabled = false; - text = "Standard Built" - } - } - - Button { - text: root.xbox ? "XBox Mapping" : "XBox not found" - property bool built: false - enabled: root.xbox && !built - onClicked: { - var mapping = Controller.newMapping(); - mapping.from(xbox.A).to(standard.A); - mapping.from(xbox.B).to(standard.B); - mapping.from(xbox.X).to(standard.X); - mapping.from(xbox.Y).to(standard.Y); - mapping.from(xbox.Up).to(standard.DU); - mapping.from(xbox.Down).to(standard.DD); - mapping.from(xbox.Left).to(standard.DL); - mapping.from(xbox.Right).to(standard.Right); - mapping.from(xbox.LB).to(standard.LB); - mapping.from(xbox.RB).to(standard.RB); - mapping.from(xbox.LS).to(standard.LS); - mapping.from(xbox.RS).to(standard.RS); - mapping.from(xbox.Start).to(standard.Start); - mapping.from(xbox.Back).to(standard.Back); - mapping.from(xbox.LY).to(standard.LY); - mapping.from(xbox.LX).to(standard.LX); - mapping.from(xbox.RY).to(standard.RY); - mapping.from(xbox.RX).to(standard.RX); - mapping.from(xbox.LT).to(standard.LT); - mapping.from(xbox.RT).to(standard.RT); - mapping.enable(); - built = false; - text = "XBox Built" - } - } - - Button { - text: root.hydra ? "Hydra Mapping" : "Hydra Not Found" - property bool built: false - enabled: root.hydra && !built - onClicked: { - var mapping = Controller.newMapping(); - mapping.from(hydra.LY).invert().to(standard.LY); - mapping.from(hydra.LX).to(standard.LX); - mapping.from(hydra.RY).invert().to(standard.RY); - mapping.from(hydra.RX).to(standard.RX); - mapping.from(hydra.LT).to(standard.LT); - mapping.from(hydra.RT).to(standard.RT); - mapping.enable(); - built = false; - text = "Hydra Built" - } - } - - Button { - text: "Test Mapping" - onClicked: { - var mapping = Controller.newMapping(); - // Inverting a value - mapping.from(hydra.RY).invert().to(standard.RY); - mapping.from(hydra.RX).to(standard.RX); - mapping.from(hydra.LY).to(standard.LY); - mapping.from(hydra.LX).to(standard.LX); - // Assigning a value from a function - // mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); - // Constrainting a value to -1, 0, or 1, with a deadzone -// mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); - mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); -// mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); -// mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); - // mapping.modifier(keyboard.Ctrl).scale(2.0) -// mapping.from(keyboard.A).to(actions.TranslateLeft) -// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) -// // First loopbacks -// // Then non-loopbacks by constraint level (number of inputs) -// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) -// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) - testMapping = mapping; - enabled = false - text = "Built" - } - } - - Button { - text: "Enable Mapping" - onClicked: root.testMapping.enable() - } - - Button { - text: "Disable Mapping" - onClicked: root.testMapping.disable() - } - - Button { - text: "Enable Mapping" - onClicked: print(Controller.getValue(root.xbox.LY)); - } - } - - Row { - Xbox { device: root.standard; label: "Standard"; width: 360 } - } - - Row { - spacing: 8 - Xbox { device: root.xbox; label: "XBox"; width: 360 } - Hydra { device: root.hydra; width: 360 } - } -// Row { -// spacing: 8 -// ScrollingGraph { -// controlId: Controller.Actions.Yaw -// label: "Yaw" -// min: -3.0 -// max: 3.0 -// size: 128 -// } -// -// ScrollingGraph { -// controlId: Controller.Actions.YAW_LEFT -// label: "Yaw Left" -// min: -3.0 -// max: 3.0 -// size: 128 -// } -// -// ScrollingGraph { -// controlId: Controller.Actions.YAW_RIGHT -// label: "Yaw Right" -// min: -3.0 -// max: 3.0 -// size: 128 -// } -// } - } -} // dialog - - - - - +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "controller" +import "controls" as HifiControls +import "styles" + +HifiControls.VrDialog { + id: root + HifiConstants { id: hifi } + title: "Controller Test" + resizable: true + contentImplicitWidth: clientArea.implicitWidth + contentImplicitHeight: clientArea.implicitHeight + backgroundColor: "beige" + + property var actions: Controller.Actions + property var standard: Controller.Standard + property var hydra: null + property var testMapping: null + property var xbox: null + + + Component.onCompleted: { + enabled = true + var xboxRegex = /^X360Controller/; + var hydraRegex = /^Hydra/; + for (var prop in Controller.Hardware) { + if(xboxRegex.test(prop)) { + root.xbox = Controller.Hardware[prop] + print("found xbox") + continue + } + if (hydraRegex.test(prop)) { + root.hydra = Controller.Hardware[prop] + print("found hydra") + continue + } + } + } + + Column { + id: clientArea + spacing: 12 + x: root.clientX + y: root.clientY + + Row { + spacing: 8 + Button { + text: "Standard Mapping" + onClicked: { + var mapping = Controller.newMapping("Default"); + mapping.from(standard.LX).to(actions.TranslateX); + mapping.from(standard.LY).to(actions.TranslateZ); + mapping.from(standard.RY).to(actions.Pitch); + mapping.from(standard.RX).to(actions.Yaw); + mapping.from(standard.DU).scale(0.5).to(actions.LONGITUDINAL_FORWARD); + mapping.from(standard.DD).scale(0.5).to(actions.LONGITUDINAL_BACKWARD); + mapping.from(standard.DL).scale(0.5).to(actions.LATERAL_LEFT); + mapping.from(standard.DR).scale(0.5).to(actions.LATERAL_RIGHT); + mapping.from(standard.X).to(actions.VERTICAL_DOWN); + mapping.from(standard.Y).to(actions.VERTICAL_UP); + mapping.from(standard.RT).scale(0.1).to(actions.BOOM_IN); + mapping.from(standard.LT).scale(0.1).to(actions.BOOM_OUT); + mapping.from(standard.B).to(actions.ACTION1); + mapping.from(standard.A).to(actions.ACTION2); + mapping.from(standard.RB).to(actions.SHIFT); + mapping.from(standard.Back).to(actions.TOGGLE_MUTE); + mapping.from(standard.Start).to(actions.CONTEXT_MENU); + Controller.enableMapping("Default"); + enabled = false; + text = "Standard Built" + } + } + + Button { + text: root.xbox ? "XBox Mapping" : "XBox not found" + property bool built: false + enabled: root.xbox && !built + onClicked: { + var mapping = Controller.newMapping(); + mapping.from(xbox.A).to(standard.A); + mapping.from(xbox.B).to(standard.B); + mapping.from(xbox.X).to(standard.X); + mapping.from(xbox.Y).to(standard.Y); + mapping.from(xbox.Up).to(standard.DU); + mapping.from(xbox.Down).to(standard.DD); + mapping.from(xbox.Left).to(standard.DL); + mapping.from(xbox.Right).to(standard.Right); + mapping.from(xbox.LB).to(standard.LB); + mapping.from(xbox.RB).to(standard.RB); + mapping.from(xbox.LS).to(standard.LS); + mapping.from(xbox.RS).to(standard.RS); + mapping.from(xbox.Start).to(standard.Start); + mapping.from(xbox.Back).to(standard.Back); + mapping.from(xbox.LY).to(standard.LY); + mapping.from(xbox.LX).to(standard.LX); + mapping.from(xbox.RY).to(standard.RY); + mapping.from(xbox.RX).to(standard.RX); + mapping.from(xbox.LT).to(standard.LT); + mapping.from(xbox.RT).to(standard.RT); + mapping.enable(); + built = false; + text = "XBox Built" + } + } + + Button { + text: root.hydra ? "Hydra Mapping" : "Hydra Not Found" + property bool built: false + enabled: root.hydra && !built + onClicked: { + var mapping = Controller.newMapping(); + mapping.from(hydra.LY).invert().to(standard.LY); + mapping.from(hydra.LX).to(standard.LX); + mapping.from(hydra.RY).invert().to(standard.RY); + mapping.from(hydra.RX).to(standard.RX); + mapping.from(hydra.LT).to(standard.LT); + mapping.from(hydra.RT).to(standard.RT); + mapping.enable(); + built = false; + text = "Hydra Built" + } + } + + Button { + text: "Test Mapping" + onClicked: { + var mapping = Controller.newMapping(); + // Inverting a value + mapping.from(hydra.RY).invert().to(standard.RY); + mapping.from(hydra.RX).to(standard.RX); + mapping.from(hydra.LY).to(standard.LY); + mapping.from(hydra.LX).to(standard.LX); + // Assigning a value from a function + // mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); + // Constrainting a value to -1, 0, or 1, with a deadzone +// mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); + mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); +// mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); +// mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); + // mapping.modifier(keyboard.Ctrl).scale(2.0) +// mapping.from(keyboard.A).to(actions.TranslateLeft) +// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) +// // First loopbacks +// // Then non-loopbacks by constraint level (number of inputs) +// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) +// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) +// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) + testMapping = mapping; + enabled = false + text = "Built" + } + } + + Button { + text: "Enable Mapping" + onClicked: root.testMapping.enable() + } + + Button { + text: "Disable Mapping" + onClicked: root.testMapping.disable() + } + + Button { + text: "Enable Mapping" + onClicked: print(Controller.getValue(root.xbox.LY)); + } + } + + Row { + Xbox { device: root.standard; label: "Standard"; width: 360 } + } + + Row { + spacing: 8 + Xbox { device: root.xbox; label: "XBox"; width: 360 } + Hydra { device: root.hydra; width: 360 } + } +// Row { +// spacing: 8 +// ScrollingGraph { +// controlId: Controller.Actions.Yaw +// label: "Yaw" +// min: -3.0 +// max: 3.0 +// size: 128 +// } +// +// ScrollingGraph { +// controlId: Controller.Actions.YAW_LEFT +// label: "Yaw Left" +// min: -3.0 +// max: 3.0 +// size: 128 +// } +// +// ScrollingGraph { +// controlId: Controller.Actions.YAW_RIGHT +// label: "Yaw Right" +// min: -3.0 +// max: 3.0 +// size: 128 +// } +// } + } +} // dialog + + + + + diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 672d6a2542..26e03b7719 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -1,512 +1,512 @@ -// -// Created by Sam Gateau on 4/27/15. -// 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 -// - -#include "UserInputMapper.h" -#include "StandardController.h" - -#include "Logging.h" - -const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - (uint16)1; -const uint16_t UserInputMapper::STANDARD_DEVICE = 0; - -// Default contruct allocate the poutput size with the current hardcoded action channels -UserInputMapper::UserInputMapper() { - registerStandardDevice(); - assignDefaulActionScales(); - createActionNames(); -} - -UserInputMapper::~UserInputMapper() { -} - -int UserInputMapper::recordDeviceOfType(const QString& deviceName) { - if (!_deviceCounts.contains(deviceName)) { - _deviceCounts[deviceName] = 0; - } - _deviceCounts[deviceName] += 1; - return _deviceCounts[deviceName]; -} - -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) { - int numberOfType = recordDeviceOfType(proxy->_name); - - if (numberOfType > 1) { - proxy->_name += QString::number(numberOfType); - } - - qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; - _registeredDevices[deviceID] = proxy; - return true; - -} - - -bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { - device->_name = "Standard"; // Just to make sure - _registeredDevices[getStandardDeviceID()] = device; - return true; -} - - -UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { - auto device = _registeredDevices.find(input.getDevice()); - if (device != _registeredDevices.end()) { - return (device->second); - } else { - return DeviceProxy::Pointer(); - } -} - -QString UserInputMapper::getDeviceName(uint16 deviceID) { - if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { - return _registeredDevices[deviceID]->_name; - } - return QString("unknown"); -} - - -void UserInputMapper::resetAllDeviceBindings() { - for (auto device : _registeredDevices) { - device.second->resetDeviceBindings(); - } -} - -void UserInputMapper::resetDevice(uint16 deviceID) { - auto device = _registeredDevices.find(deviceID); - if (device != _registeredDevices.end()) { - device->second->resetDeviceBindings(); - } -} - -int UserInputMapper::findDevice(QString name) const { - for (auto device : _registeredDevices) { - if (device.second->_name.split(" (")[0] == name) { - return device.first; - } else if (device.second->_baseName == name) { - return device.first; - } - } - return Input::INVALID_DEVICE; -} - -QVector UserInputMapper::getDeviceNames() { - QVector result; - for (auto device : _registeredDevices) { - QString deviceName = device.second->_name.split(" (")[0]; - result << deviceName; - } - return result; -} - -UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { - - // Split the full input name as such: deviceName.inputName - auto names = inputName.split('.'); - - if (names.size() >= 2) { - // Get the device name: - auto deviceName = names[0]; - auto inputName = names[1]; - - int deviceID = findDevice(deviceName); - if (deviceID != Input::INVALID_DEVICE) { - const auto& deviceProxy = _registeredDevices.at(deviceID); - auto deviceInputs = deviceProxy->getAvailabeInputs(); - - for (auto input : deviceInputs) { - if (input.second == inputName) { - return input.first; - } - } - - qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; - - } else if (deviceName == "Actions") { - deviceID = ACTIONS_DEVICE; - int actionNum = 0; - for (auto action : _actionNames) { - if (action == inputName) { - return Input(ACTIONS_DEVICE, actionNum, ChannelType::AXIS); - } - actionNum++; - } - - qCDebug(controllers) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions"; - - } else { - qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">"; - } - } else { - qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; - } - - return Input::INVALID_INPUT; -} - - - -bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { - return addInputChannel(action, input, Input(), scale); -} - -bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { - // Check that the device is registered - if (!getDeviceProxy(input)) { - qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; - return false; - } - - auto inputChannel = InputChannel(input, modifier, action, scale); - - // Insert or replace the input to modifiers - if (inputChannel.hasModifier()) { - auto& modifiers = _inputToModifiersMap[input.getID()]; - modifiers.push_back(inputChannel._modifier); - std::sort(modifiers.begin(), modifiers.end()); - } - - // Now update the action To Inputs side of things - _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); - - return true; -} - -int UserInputMapper::addInputChannels(const InputChannels& channels) { - int nbAdded = 0; - for (auto& channel : channels) { - nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); - } - return nbAdded; -} - -bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { - // Remove from Input to Modifiers map - if (inputChannel.hasModifier()) { - _inputToModifiersMap.erase(inputChannel._input.getID()); - } - - // Remove from Action to Inputs map - std::pair ret; - ret = _actionToInputsMap.equal_range(inputChannel._action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - if (it->second == inputChannel) { - _actionToInputsMap.erase(it); - return true; - } - } - - return false; -} - -void UserInputMapper::removeAllInputChannels() { - _inputToModifiersMap.clear(); - _actionToInputsMap.clear(); -} - -void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { - QVector channels = getAllInputsForDevice(device); - for (auto& channel : channels) { - removeInputChannel(channel); - } -} - -void UserInputMapper::removeDevice(int device) { - removeAllInputChannelsForDevice((uint16) device); - _registeredDevices.erase(device); -} - -int UserInputMapper::getInputChannels(InputChannels& channels) const { - for (auto& channel : _actionToInputsMap) { - channels.push_back(channel.second); - } - - return _actionToInputsMap.size(); -} - -QVector UserInputMapper::getAllInputsForDevice(uint16 device) { - InputChannels allChannels; - getInputChannels(allChannels); - - QVector channels; - for (InputChannel inputChannel : allChannels) { - if (inputChannel._input._device == device) { - channels.push_back(inputChannel); - } - } - - return channels; -} - -void fixBisectedAxis(float& full, float& negative, float& positive) { - full = full + (negative * -1.0f) + positive; - negative = full >= 0.0f ? 0.0f : full * -1.0f; - positive = full <= 0.0f ? 0.0f : full; -} - -void UserInputMapper::update(float deltaTime) { - - // Reset the axis state for next loop - for (auto& channel : _actionStates) { - channel = 0.0f; - } - - for (auto& channel : _poseStates) { - channel = PoseValue(); - } - - int currentTimestamp = 0; - for (auto& channelInput : _actionToInputsMap) { - auto& inputMapping = channelInput.second; - auto& inputID = inputMapping._input; - bool enabled = true; - - // Check if this input channel has modifiers and collect the possibilities - auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); - if (modifiersIt != _inputToModifiersMap.end()) { - Modifiers validModifiers; - bool isActiveModifier = false; - for (auto& modifier : modifiersIt->second) { - auto deviceProxy = getDeviceProxy(modifier); - if (deviceProxy->getButton(modifier, currentTimestamp)) { - validModifiers.push_back(modifier); - isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); - } - } - enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; - } - - // if enabled: default input or all modifiers on - if (enabled) { - auto deviceProxy = getDeviceProxy(inputID); - switch (inputMapping._input.getType()) { - case ChannelType::BUTTON: { - _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime - break; - } - case ChannelType::AXIS: { - _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); - break; - } - case ChannelType::POSE: { - if (!_poseStates[channelInput.first].isValid()) { - _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); - } - break; - } - default: { - break; //silence please - } - } - } else{ - // Channel input not enabled - enabled = false; - } - } - - // Scale all the channel step with the scale - for (auto i = 0; i < NUM_ACTIONS; i++) { - if (_externalActionStates[i] != 0) { - _actionStates[i] += _externalActionStates[i]; - _externalActionStates[i] = 0.0f; - } - } - - // merge the bisected and non-bisected axes for now - fixBisectedAxis(_actionStates[TRANSLATE_X], _actionStates[LATERAL_LEFT], _actionStates[LATERAL_RIGHT]); - fixBisectedAxis(_actionStates[TRANSLATE_Y], _actionStates[VERTICAL_DOWN], _actionStates[VERTICAL_UP]); - fixBisectedAxis(_actionStates[TRANSLATE_Z], _actionStates[LONGITUDINAL_FORWARD], _actionStates[LONGITUDINAL_BACKWARD]); - fixBisectedAxis(_actionStates[TRANSLATE_CAMERA_Z], _actionStates[BOOM_IN], _actionStates[BOOM_OUT]); - fixBisectedAxis(_actionStates[ROTATE_Y], _actionStates[YAW_LEFT], _actionStates[YAW_RIGHT]); - fixBisectedAxis(_actionStates[ROTATE_X], _actionStates[PITCH_UP], _actionStates[PITCH_DOWN]); - - - static const float EPSILON = 0.01f; - for (auto i = 0; i < NUM_ACTIONS; i++) { - _actionStates[i] *= _actionScales[i]; - // Emit only on change, and emit when moving back to 0 - if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { - _lastActionStates[i] = _actionStates[i]; - emit actionEvent(i, _actionStates[i]); - } - // TODO: emit signal for pose changes - } -} - -QVector UserInputMapper::getAllActions() const { - QVector actions; - for (auto i = 0; i < NUM_ACTIONS; i++) { - actions.append(Action(i)); - } - return actions; -} - -QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { - QVector inputChannels; - std::pair ret; - ret = _actionToInputsMap.equal_range(action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - inputChannels.append(it->second); - } - return inputChannels; -} - -int UserInputMapper::findAction(const QString& actionName) const { - auto actions = getAllActions(); - for (auto action : actions) { - if (getActionName(action) == actionName) { - return action; - } - } - // If the action isn't found, return -1 - return -1; -} - -QVector UserInputMapper::getActionNames() const { - QVector result; - for (auto i = 0; i < NUM_ACTIONS; i++) { - result << _actionNames[i]; - } - return result; -} - -void UserInputMapper::assignDefaulActionScales() { - _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit - _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit - _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit - _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit - _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit - _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit - _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit - _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit - _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit - _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit - _actionScales[BOOM_IN] = 0.5f; // .5m per unit - _actionScales[BOOM_OUT] = 0.5f; // .5m per unit - _actionScales[LEFT_HAND] = 1.0f; // default - _actionScales[RIGHT_HAND] = 1.0f; // default - _actionScales[LEFT_HAND_CLICK] = 1.0f; // on - _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on - _actionScales[SHIFT] = 1.0f; // on - _actionScales[ACTION1] = 1.0f; // default - _actionScales[ACTION2] = 1.0f; // default - _actionScales[TRANSLATE_X] = 1.0f; // default - _actionScales[TRANSLATE_Y] = 1.0f; // default - _actionScales[TRANSLATE_Z] = 1.0f; // default - _actionScales[ROLL] = 1.0f; // default - _actionScales[PITCH] = 1.0f; // default - _actionScales[YAW] = 1.0f; // default -} - -// This is only necessary as long as the actions are hardcoded -// Eventually you can just add the string when you add the action -void UserInputMapper::createActionNames() { - _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; - _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; - _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; - _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; - _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; - _actionNames[VERTICAL_UP] = "VERTICAL_UP"; - _actionNames[YAW_LEFT] = "YAW_LEFT"; - _actionNames[YAW_RIGHT] = "YAW_RIGHT"; - _actionNames[PITCH_DOWN] = "PITCH_DOWN"; - _actionNames[PITCH_UP] = "PITCH_UP"; - _actionNames[BOOM_IN] = "BOOM_IN"; - _actionNames[BOOM_OUT] = "BOOM_OUT"; - _actionNames[LEFT_HAND] = "LEFT_HAND"; - _actionNames[RIGHT_HAND] = "RIGHT_HAND"; - _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; - _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; - _actionNames[SHIFT] = "SHIFT"; - _actionNames[ACTION1] = "ACTION1"; - _actionNames[ACTION2] = "ACTION2"; - _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; - _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; - _actionNames[TRANSLATE_X] = "TranslateX"; - _actionNames[TRANSLATE_Y] = "TranslateY"; - _actionNames[TRANSLATE_Z] = "TranslateZ"; - _actionNames[ROLL] = "Roll"; - _actionNames[PITCH] = "Pitch"; - _actionNames[YAW] = "Yaw"; -} - -void UserInputMapper::registerStandardDevice() { - _standardController = std::make_shared(); - _standardController->registerToUserInputMapper(*this); - _standardController->assignDefaultInputMapping(*this); -} - -static int actionMetaTypeId = qRegisterMetaType(); -static int inputMetaTypeId = qRegisterMetaType(); -static int inputPairMetaTypeId = qRegisterMetaType(); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); -void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); -QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); -void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); -void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { - QScriptValue obj = engine->newObject(); - obj.setProperty("device", input.getDevice()); - obj.setProperty("channel", input.getChannel()); - obj.setProperty("type", (unsigned short)input.getType()); - obj.setProperty("id", input.getID()); - return obj; -} - -void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { - input.setDevice(object.property("device").toUInt16()); - input.setChannel(object.property("channel").toUInt16()); - input.setType(object.property("type").toUInt16()); - input.setID(object.property("id").toInt32()); -} - -QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { - QScriptValue obj = engine->newObject(); - auto userInputMapper = DependencyManager::get(); - obj.setProperty("action", (int)action); - obj.setProperty("actionName", userInputMapper->getActionName(action)); - return obj; -} - -void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { - action = UserInputMapper::Action(object.property("action").toVariant().toInt()); -} - -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { - QScriptValue obj = engine->newObject(); - obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); - obj.setProperty("inputName", inputPair.second); - return obj; -} - -void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { - inputFromScriptValue(object.property("input"), inputPair.first); - inputPair.second = QString(object.property("inputName").toVariant().toString()); -} - -void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); - qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); - qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); -} - -UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { - return Input(STANDARD_DEVICE, button, ChannelType::BUTTON); -} - -UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) { - return Input(STANDARD_DEVICE, axis, ChannelType::AXIS); -} - -UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { - return Input(STANDARD_DEVICE, pose, ChannelType::POSE); -} +// +// Created by Sam Gateau on 4/27/15. +// 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 +// + +#include "UserInputMapper.h" +#include "StandardController.h" + +#include "Logging.h" + +const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - (uint16)1; +const uint16_t UserInputMapper::STANDARD_DEVICE = 0; + +// Default contruct allocate the poutput size with the current hardcoded action channels +UserInputMapper::UserInputMapper() { + registerStandardDevice(); + assignDefaulActionScales(); + createActionNames(); +} + +UserInputMapper::~UserInputMapper() { +} + +int UserInputMapper::recordDeviceOfType(const QString& deviceName) { + if (!_deviceCounts.contains(deviceName)) { + _deviceCounts[deviceName] = 0; + } + _deviceCounts[deviceName] += 1; + return _deviceCounts[deviceName]; +} + +bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) { + int numberOfType = recordDeviceOfType(proxy->_name); + + if (numberOfType > 1) { + proxy->_name += QString::number(numberOfType); + } + + qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; + _registeredDevices[deviceID] = proxy; + return true; + +} + + +bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { + device->_name = "Standard"; // Just to make sure + _registeredDevices[getStandardDeviceID()] = device; + return true; +} + + +UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { + auto device = _registeredDevices.find(input.getDevice()); + if (device != _registeredDevices.end()) { + return (device->second); + } else { + return DeviceProxy::Pointer(); + } +} + +QString UserInputMapper::getDeviceName(uint16 deviceID) { + if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { + return _registeredDevices[deviceID]->_name; + } + return QString("unknown"); +} + + +void UserInputMapper::resetAllDeviceBindings() { + for (auto device : _registeredDevices) { + device.second->resetDeviceBindings(); + } +} + +void UserInputMapper::resetDevice(uint16 deviceID) { + auto device = _registeredDevices.find(deviceID); + if (device != _registeredDevices.end()) { + device->second->resetDeviceBindings(); + } +} + +int UserInputMapper::findDevice(QString name) const { + for (auto device : _registeredDevices) { + if (device.second->_name.split(" (")[0] == name) { + return device.first; + } else if (device.second->_baseName == name) { + return device.first; + } + } + return Input::INVALID_DEVICE; +} + +QVector UserInputMapper::getDeviceNames() { + QVector result; + for (auto device : _registeredDevices) { + QString deviceName = device.second->_name.split(" (")[0]; + result << deviceName; + } + return result; +} + +UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { + + // Split the full input name as such: deviceName.inputName + auto names = inputName.split('.'); + + if (names.size() >= 2) { + // Get the device name: + auto deviceName = names[0]; + auto inputName = names[1]; + + int deviceID = findDevice(deviceName); + if (deviceID != Input::INVALID_DEVICE) { + const auto& deviceProxy = _registeredDevices.at(deviceID); + auto deviceInputs = deviceProxy->getAvailabeInputs(); + + for (auto input : deviceInputs) { + if (input.second == inputName) { + return input.first; + } + } + + qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; + + } else if (deviceName == "Actions") { + deviceID = ACTIONS_DEVICE; + int actionNum = 0; + for (auto action : _actionNames) { + if (action == inputName) { + return Input(ACTIONS_DEVICE, actionNum, ChannelType::AXIS); + } + actionNum++; + } + + qCDebug(controllers) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions"; + + } else { + qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">"; + } + } else { + qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; + } + + return Input::INVALID_INPUT; +} + + + +bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { + return addInputChannel(action, input, Input(), scale); +} + +bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { + // Check that the device is registered + if (!getDeviceProxy(input)) { + qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; + return false; + } + + auto inputChannel = InputChannel(input, modifier, action, scale); + + // Insert or replace the input to modifiers + if (inputChannel.hasModifier()) { + auto& modifiers = _inputToModifiersMap[input.getID()]; + modifiers.push_back(inputChannel._modifier); + std::sort(modifiers.begin(), modifiers.end()); + } + + // Now update the action To Inputs side of things + _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); + + return true; +} + +int UserInputMapper::addInputChannels(const InputChannels& channels) { + int nbAdded = 0; + for (auto& channel : channels) { + nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); + } + return nbAdded; +} + +bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { + // Remove from Input to Modifiers map + if (inputChannel.hasModifier()) { + _inputToModifiersMap.erase(inputChannel._input.getID()); + } + + // Remove from Action to Inputs map + std::pair ret; + ret = _actionToInputsMap.equal_range(inputChannel._action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + if (it->second == inputChannel) { + _actionToInputsMap.erase(it); + return true; + } + } + + return false; +} + +void UserInputMapper::removeAllInputChannels() { + _inputToModifiersMap.clear(); + _actionToInputsMap.clear(); +} + +void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { + QVector channels = getAllInputsForDevice(device); + for (auto& channel : channels) { + removeInputChannel(channel); + } +} + +void UserInputMapper::removeDevice(int device) { + removeAllInputChannelsForDevice((uint16) device); + _registeredDevices.erase(device); +} + +int UserInputMapper::getInputChannels(InputChannels& channels) const { + for (auto& channel : _actionToInputsMap) { + channels.push_back(channel.second); + } + + return _actionToInputsMap.size(); +} + +QVector UserInputMapper::getAllInputsForDevice(uint16 device) { + InputChannels allChannels; + getInputChannels(allChannels); + + QVector channels; + for (InputChannel inputChannel : allChannels) { + if (inputChannel._input._device == device) { + channels.push_back(inputChannel); + } + } + + return channels; +} + +void fixBisectedAxis(float& full, float& negative, float& positive) { + full = full + (negative * -1.0f) + positive; + negative = full >= 0.0f ? 0.0f : full * -1.0f; + positive = full <= 0.0f ? 0.0f : full; +} + +void UserInputMapper::update(float deltaTime) { + + // Reset the axis state for next loop + for (auto& channel : _actionStates) { + channel = 0.0f; + } + + for (auto& channel : _poseStates) { + channel = PoseValue(); + } + + int currentTimestamp = 0; + for (auto& channelInput : _actionToInputsMap) { + auto& inputMapping = channelInput.second; + auto& inputID = inputMapping._input; + bool enabled = true; + + // Check if this input channel has modifiers and collect the possibilities + auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); + if (modifiersIt != _inputToModifiersMap.end()) { + Modifiers validModifiers; + bool isActiveModifier = false; + for (auto& modifier : modifiersIt->second) { + auto deviceProxy = getDeviceProxy(modifier); + if (deviceProxy->getButton(modifier, currentTimestamp)) { + validModifiers.push_back(modifier); + isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); + } + } + enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; + } + + // if enabled: default input or all modifiers on + if (enabled) { + auto deviceProxy = getDeviceProxy(inputID); + switch (inputMapping._input.getType()) { + case ChannelType::BUTTON: { + _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime + break; + } + case ChannelType::AXIS: { + _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); + break; + } + case ChannelType::POSE: { + if (!_poseStates[channelInput.first].isValid()) { + _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); + } + break; + } + default: { + break; //silence please + } + } + } else{ + // Channel input not enabled + enabled = false; + } + } + + // Scale all the channel step with the scale + for (auto i = 0; i < NUM_ACTIONS; i++) { + if (_externalActionStates[i] != 0) { + _actionStates[i] += _externalActionStates[i]; + _externalActionStates[i] = 0.0f; + } + } + + // merge the bisected and non-bisected axes for now + fixBisectedAxis(_actionStates[TRANSLATE_X], _actionStates[LATERAL_LEFT], _actionStates[LATERAL_RIGHT]); + fixBisectedAxis(_actionStates[TRANSLATE_Y], _actionStates[VERTICAL_DOWN], _actionStates[VERTICAL_UP]); + fixBisectedAxis(_actionStates[TRANSLATE_Z], _actionStates[LONGITUDINAL_FORWARD], _actionStates[LONGITUDINAL_BACKWARD]); + fixBisectedAxis(_actionStates[TRANSLATE_CAMERA_Z], _actionStates[BOOM_IN], _actionStates[BOOM_OUT]); + fixBisectedAxis(_actionStates[ROTATE_Y], _actionStates[YAW_LEFT], _actionStates[YAW_RIGHT]); + fixBisectedAxis(_actionStates[ROTATE_X], _actionStates[PITCH_UP], _actionStates[PITCH_DOWN]); + + + static const float EPSILON = 0.01f; + for (auto i = 0; i < NUM_ACTIONS; i++) { + _actionStates[i] *= _actionScales[i]; + // Emit only on change, and emit when moving back to 0 + if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { + _lastActionStates[i] = _actionStates[i]; + emit actionEvent(i, _actionStates[i]); + } + // TODO: emit signal for pose changes + } +} + +QVector UserInputMapper::getAllActions() const { + QVector actions; + for (auto i = 0; i < NUM_ACTIONS; i++) { + actions.append(Action(i)); + } + return actions; +} + +QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { + QVector inputChannels; + std::pair ret; + ret = _actionToInputsMap.equal_range(action); + for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { + inputChannels.append(it->second); + } + return inputChannels; +} + +int UserInputMapper::findAction(const QString& actionName) const { + auto actions = getAllActions(); + for (auto action : actions) { + if (getActionName(action) == actionName) { + return action; + } + } + // If the action isn't found, return -1 + return -1; +} + +QVector UserInputMapper::getActionNames() const { + QVector result; + for (auto i = 0; i < NUM_ACTIONS; i++) { + result << _actionNames[i]; + } + return result; +} + +void UserInputMapper::assignDefaulActionScales() { + _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit + _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit + _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit + _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit + _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit + _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit + _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit + _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit + _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit + _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit + _actionScales[BOOM_IN] = 0.5f; // .5m per unit + _actionScales[BOOM_OUT] = 0.5f; // .5m per unit + _actionScales[LEFT_HAND] = 1.0f; // default + _actionScales[RIGHT_HAND] = 1.0f; // default + _actionScales[LEFT_HAND_CLICK] = 1.0f; // on + _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on + _actionScales[SHIFT] = 1.0f; // on + _actionScales[ACTION1] = 1.0f; // default + _actionScales[ACTION2] = 1.0f; // default + _actionScales[TRANSLATE_X] = 1.0f; // default + _actionScales[TRANSLATE_Y] = 1.0f; // default + _actionScales[TRANSLATE_Z] = 1.0f; // default + _actionScales[ROLL] = 1.0f; // default + _actionScales[PITCH] = 1.0f; // default + _actionScales[YAW] = 1.0f; // default +} + +// This is only necessary as long as the actions are hardcoded +// Eventually you can just add the string when you add the action +void UserInputMapper::createActionNames() { + _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; + _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; + _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; + _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; + _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; + _actionNames[VERTICAL_UP] = "VERTICAL_UP"; + _actionNames[YAW_LEFT] = "YAW_LEFT"; + _actionNames[YAW_RIGHT] = "YAW_RIGHT"; + _actionNames[PITCH_DOWN] = "PITCH_DOWN"; + _actionNames[PITCH_UP] = "PITCH_UP"; + _actionNames[BOOM_IN] = "BOOM_IN"; + _actionNames[BOOM_OUT] = "BOOM_OUT"; + _actionNames[LEFT_HAND] = "LEFT_HAND"; + _actionNames[RIGHT_HAND] = "RIGHT_HAND"; + _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; + _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; + _actionNames[SHIFT] = "SHIFT"; + _actionNames[ACTION1] = "ACTION1"; + _actionNames[ACTION2] = "ACTION2"; + _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; + _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; + _actionNames[TRANSLATE_X] = "TranslateX"; + _actionNames[TRANSLATE_Y] = "TranslateY"; + _actionNames[TRANSLATE_Z] = "TranslateZ"; + _actionNames[ROLL] = "Roll"; + _actionNames[PITCH] = "Pitch"; + _actionNames[YAW] = "Yaw"; +} + +void UserInputMapper::registerStandardDevice() { + _standardController = std::make_shared(); + _standardController->registerToUserInputMapper(*this); + _standardController->assignDefaultInputMapping(*this); +} + +static int actionMetaTypeId = qRegisterMetaType(); +static int inputMetaTypeId = qRegisterMetaType(); +static int inputPairMetaTypeId = qRegisterMetaType(); + +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); + +QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { + QScriptValue obj = engine->newObject(); + obj.setProperty("device", input.getDevice()); + obj.setProperty("channel", input.getChannel()); + obj.setProperty("type", (unsigned short)input.getType()); + obj.setProperty("id", input.getID()); + return obj; +} + +void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { + input.setDevice(object.property("device").toUInt16()); + input.setChannel(object.property("channel").toUInt16()); + input.setType(object.property("type").toUInt16()); + input.setID(object.property("id").toInt32()); +} + +QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { + QScriptValue obj = engine->newObject(); + auto userInputMapper = DependencyManager::get(); + obj.setProperty("action", (int)action); + obj.setProperty("actionName", userInputMapper->getActionName(action)); + return obj; +} + +void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { + action = UserInputMapper::Action(object.property("action").toVariant().toInt()); +} + +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { + QScriptValue obj = engine->newObject(); + obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); + obj.setProperty("inputName", inputPair.second); + return obj; +} + +void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { + inputFromScriptValue(object.property("input"), inputPair.first); + inputPair.second = QString(object.property("inputName").toVariant().toString()); +} + +void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); + qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); + qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); +} + +UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { + return Input(STANDARD_DEVICE, button, ChannelType::BUTTON); +} + +UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) { + return Input(STANDARD_DEVICE, axis, ChannelType::AXIS); +} + +UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { + return Input(STANDARD_DEVICE, pose, ChannelType::POSE); +} diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index 9d9ac8bc26..d3e4e7a629 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -1,207 +1,207 @@ -// -// Joystick.cpp -// input-plugins/src/input-plugins -// -// Created by Stephen Birarda on 2014-09-23. -// Copyright 2014 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 -// - -#include "Joystick.h" - -#include -#include - -const float CONTROLLER_THRESHOLD = 0.3f; - -#ifdef HAVE_SDL2 -const float MAX_AXIS = 32768.0f; - -Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : - InputDevice(name), - _sdlGameController(sdlGameController), - _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), - _instanceId(instanceId) -{ - -} - -#endif - -Joystick::~Joystick() { - closeJoystick(); -} - -void Joystick::closeJoystick() { -#ifdef HAVE_SDL2 - SDL_GameControllerClose(_sdlGameController); -#endif -} - -void Joystick::update(float deltaTime, bool jointsCaptured) { - for (auto axisState : _axisStateMap) { - if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { - _axisStateMap[axisState.first] = 0.0f; - } - } -} - -void Joystick::focusOutEvent() { - _axisStateMap.clear(); - _buttonPressedMap.clear(); -}; - -#ifdef HAVE_SDL2 - -void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { - SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; - _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; -} - -void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { - auto input = makeInput((controller::StandardButtonChannel)event.button); - bool newValue = event.state == SDL_PRESSED; - if (newValue) { - _buttonPressedMap.insert(input.getChannel()); - } else { - _buttonPressedMap.erase(input.getChannel()); - } -} - -#endif - - -void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - // Buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); - - // DPad - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); - - // Bumpers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); - - // Stick press - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); - - // Center buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); - - // Analog sticks - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); - - // Triggers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); - - // Aliases, PlayStation style names - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); - - return availableInputs; - }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerDevice(_deviceID, proxy); -} - - -void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { -#if 0 -#ifdef HAVE_SDL2 - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED); - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), BOOM_SPEED); - - // Hold front right shoulder button for precision controls - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); -#endif -#endif +// +// Joystick.cpp +// input-plugins/src/input-plugins +// +// Created by Stephen Birarda on 2014-09-23. +// Copyright 2014 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 +// + +#include "Joystick.h" + +#include +#include + +const float CONTROLLER_THRESHOLD = 0.3f; + +#ifdef HAVE_SDL2 +const float MAX_AXIS = 32768.0f; + +Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : + InputDevice(name), + _sdlGameController(sdlGameController), + _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), + _instanceId(instanceId) +{ + +} + +#endif + +Joystick::~Joystick() { + closeJoystick(); +} + +void Joystick::closeJoystick() { +#ifdef HAVE_SDL2 + SDL_GameControllerClose(_sdlGameController); +#endif +} + +void Joystick::update(float deltaTime, bool jointsCaptured) { + for (auto axisState : _axisStateMap) { + if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { + _axisStateMap[axisState.first] = 0.0f; + } + } +} + +void Joystick::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + +#ifdef HAVE_SDL2 + +void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { + SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; + _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; +} + +void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { + auto input = makeInput((controller::StandardButtonChannel)event.button); + bool newValue = event.state == SDL_PRESSED; + if (newValue) { + _buttonPressedMap.insert(input.getChannel()); + } else { + _buttonPressedMap.erase(input.getChannel()); + } +} + +#endif + + +void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { + // Grab the current free device ID + _deviceID = mapper.getFreeDeviceID(); + + auto proxy = std::make_shared(_name); + proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; + // Buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); + + // DPad + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); + + // Bumpers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); + + // Stick press + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); + + // Center buttons + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); + + // Analog sticks + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); + + // Triggers + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); + + // Aliases, PlayStation style names + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); + + return availableInputs; + }; + proxy->resetDeviceBindings = [this, &mapper] () -> bool { + mapper.removeAllInputChannelsForDevice(_deviceID); + this->assignDefaultInputMapping(mapper); + return true; + }; + mapper.registerDevice(_deviceID, proxy); +} + + +void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { +#if 0 +#ifdef HAVE_SDL2 + const float JOYSTICK_MOVE_SPEED = 1.0f; + const float DPAD_MOVE_SPEED = 0.5f; + const float JOYSTICK_YAW_SPEED = 0.5f; + const float JOYSTICK_PITCH_SPEED = 0.25f; + const float BOOM_SPEED = 0.1f; + + // Y axes are flipped (up is negative) + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED); + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), BOOM_SPEED); + + // Hold front right shoulder button for precision controls + // Left Joystick: Movement, strafing + mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); + + // Right Joystick: Camera orientation + mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); + + // Dpad movement + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + + // Button controls + mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); + + // Zoom + mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); + mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); + + mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); + + mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); + mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); +#endif +#endif } \ No newline at end of file diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index ad99774737..38f00f4f15 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -1,70 +1,70 @@ -// -// Joystick.h -// input-plugins/src/input-plugins -// -// Created by Stephen Birarda on 2014-09-23. -// Copyright 2014 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 -// - -#ifndef hifi_Joystick_h -#define hifi_Joystick_h - -#include -#include - -#ifdef HAVE_SDL2 -#include -#undef main -#endif - -#include -#include - -class Joystick : public QObject, public InputDevice { - Q_OBJECT - Q_PROPERTY(QString name READ getName) - -#ifdef HAVE_SDL2 - Q_PROPERTY(int instanceId READ getInstanceId) -#endif - -public: - - const QString& getName() const { return _name; } - - // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - - Joystick() : InputDevice("Joystick") {} - ~Joystick(); - -#ifdef HAVE_SDL2 - Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); -#endif - - void closeJoystick(); - -#ifdef HAVE_SDL2 - void handleAxisEvent(const SDL_ControllerAxisEvent& event); - void handleButtonEvent(const SDL_ControllerButtonEvent& event); -#endif - -#ifdef HAVE_SDL2 - int getInstanceId() const { return _instanceId; } -#endif - -private: -#ifdef HAVE_SDL2 - SDL_GameController* _sdlGameController; - SDL_Joystick* _sdlJoystick; - SDL_JoystickID _instanceId; -#endif -}; - -#endif // hifi_Joystick_h +// +// Joystick.h +// input-plugins/src/input-plugins +// +// Created by Stephen Birarda on 2014-09-23. +// Copyright 2014 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 +// + +#ifndef hifi_Joystick_h +#define hifi_Joystick_h + +#include +#include + +#ifdef HAVE_SDL2 +#include +#undef main +#endif + +#include +#include + +class Joystick : public QObject, public InputDevice { + Q_OBJECT + Q_PROPERTY(QString name READ getName) + +#ifdef HAVE_SDL2 + Q_PROPERTY(int instanceId READ getInstanceId) +#endif + +public: + + const QString& getName() const { return _name; } + + // Device functions + virtual void registerToUserInputMapper(UserInputMapper& mapper) override; + virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + Joystick() : InputDevice("Joystick") {} + ~Joystick(); + +#ifdef HAVE_SDL2 + Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); +#endif + + void closeJoystick(); + +#ifdef HAVE_SDL2 + void handleAxisEvent(const SDL_ControllerAxisEvent& event); + void handleButtonEvent(const SDL_ControllerButtonEvent& event); +#endif + +#ifdef HAVE_SDL2 + int getInstanceId() const { return _instanceId; } +#endif + +private: +#ifdef HAVE_SDL2 + SDL_GameController* _sdlGameController; + SDL_Joystick* _sdlJoystick; + SDL_JoystickID _instanceId; +#endif +}; + +#endif // hifi_Joystick_h From 3d8b7f9d108721605821fbc853108261f88ecb70 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 19 Oct 2015 12:56:06 -0700 Subject: [PATCH 062/232] Avoid to create a route when the source is not defined --- .../src/controllers/ScriptingInterface.cpp | 2 +- .../controllers/impl/MappingBuilderProxy.cpp | 23 +++++++++++-------- libraries/entities/src/PolyLineEntityItem.cpp | 2 +- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index c5c7969604..3e470fd365 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -300,7 +300,7 @@ namespace controller { const auto& destination = route->_destination; // THis could happen if the route destination failed to create // FIXME: Maybe do not create the route if the destination failed and avoid this case ? - if (!destination) { + if (!source || !destination) { continue; } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index e75068b311..bde10defdc 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -39,9 +39,14 @@ QObject* MappingBuilderProxy::from(const QScriptValue& source) { } QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { - auto route = Route::Pointer(new Route()); - route->_source = source; - return new RouteBuilderProxy(_parent, _mapping, route); + if (source) { + auto route = Route::Pointer(new Route()); + route->_source = source; + return new RouteBuilderProxy(_parent, _mapping, route); + } else { + qCDebug(controllers) << "MappingBuilderProxy::from : source is null so no route created"; + return nullptr; + } } QObject* MappingBuilderProxy::makeAxis(const QJSValue& source1, const QJSValue& source2) { @@ -60,16 +65,16 @@ const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); void MappingBuilderProxy::parse(const QJsonObject& json) { _mapping->_name = json[JSON_NAME].toString(); - _mapping->_channelMappings.clear(); - const auto& jsonChannels = json[JSON_CHANNELS].toArray(); - for (const auto& channelIt : jsonChannels) { + _mapping->_channelMappings.clear(); + const auto& jsonChannels = json[JSON_CHANNELS].toArray(); + for (const auto& channelIt : jsonChannels) { parseRoute(channelIt); } } void MappingBuilderProxy::parseRoute(const QJsonValue& json) { if (json.isObject()) { - const auto& jsonChannel = json.toObject(); + const auto& jsonChannel = json.toObject(); auto newRoute = from(jsonChannel[JSON_CHANNEL_FROM]); if (newRoute) { @@ -81,8 +86,8 @@ void MappingBuilderProxy::parseRoute(const QJsonValue& json) { } QObject* MappingBuilderProxy::from(const QJsonValue& json) { - if (json.isString()) { - return from(_parent.endpointFor(_parent.inputFor(json.toString()))); + if (json.isString()) { + return from(_parent.endpointFor(_parent.inputFor(json.toString()))); } else if (json.isObject()) { // Endpoint is defined as an object, we expect a js function then return nullptr; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 352d0425bf..e2b55302d1 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -108,7 +108,7 @@ bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { bool PolyLineEntityItem::setNormals(const QVector& normals) { _normals = normals; - if (_points.size() < 2 || _normals.size() < 2) { + if (_points.size() < 2 || _normals.size() < 2 || _strokeWidths.size() < 2) { return false; } From 5a0bb4c31bd7717b959aae9d2219a74bc8ba0d93 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 16:51:31 -0700 Subject: [PATCH 063/232] Fixing line endings --- tests/controllers/src/main.cpp | 300 ++++++++++++++++----------------- 1 file changed, 150 insertions(+), 150 deletions(-) diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index ccd670640c..eb080700fa 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -1,158 +1,158 @@ -// -// main.cpp -// tests/gpu-test/src -// -// 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 -// - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include +// +// main.cpp +// tests/gpu-test/src +// +// 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 +// + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include #include #include - -#include -#include -#include -#include -#include -#include - -#include -#include - -const QString& getResourcesDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; - qDebug() << "Resources Path: " << dir; - } - return dir; -} - -const QString& getQmlDir() { - static QString dir; - if (dir.isEmpty()) { - dir = getResourcesDir() + "qml/"; - qDebug() << "Qml Path: " << dir; - } - return dir; -} - -const QString& getTestQmlDir() { - static QString dir; - if (dir.isEmpty()) { - QDir path(__FILE__); - path.cdUp(); - dir = path.cleanPath(path.absoluteFilePath("../")) + "/qml/"; - qDebug() << "Qml Test Path: " << dir; - } - return dir; -} - -using namespace controller; - - -class PluginContainerProxy : public QObject, PluginContainer { - Q_OBJECT -public: - PluginContainerProxy() { - Plugin::setContainer(this); - } - virtual ~PluginContainerProxy() {} - virtual void addMenu(const QString& menuName) override {} - virtual void removeMenu(const QString& menuName) override {} - virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; } - virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {} - virtual bool isOptionChecked(const QString& name) override { return false; } - virtual void setIsOptionChecked(const QString& path, bool checked) override {} - virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {} - virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {} - virtual void showDisplayPluginsTools() override {} - virtual void requestReset() override {} - virtual QGLWidget* getPrimarySurface() override { return nullptr; } - virtual bool isForeground() override { return true; } - virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } -}; - -class MyControllerScriptingInterface : public controller::ScriptingInterface { -public: - virtual void registerControllerTypes(QScriptEngine* engine) {}; -}; - - -int main(int argc, char** argv) { - QGuiApplication app(argc, argv); + +#include +#include +#include +#include +#include +#include + +#include +#include + +const QString& getResourcesDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; + qDebug() << "Resources Path: " << dir; + } + return dir; +} + +const QString& getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + dir = getResourcesDir() + "qml/"; + qDebug() << "Qml Path: " << dir; + } + return dir; +} + +const QString& getTestQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../")) + "/qml/"; + qDebug() << "Qml Test Path: " << dir; + } + return dir; +} + +using namespace controller; + + +class PluginContainerProxy : public QObject, PluginContainer { + Q_OBJECT +public: + PluginContainerProxy() { + Plugin::setContainer(this); + } + virtual ~PluginContainerProxy() {} + virtual void addMenu(const QString& menuName) override {} + virtual void removeMenu(const QString& menuName) override {} + virtual QAction* addMenuItem(const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; } + virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {} + virtual bool isOptionChecked(const QString& name) override { return false; } + virtual void setIsOptionChecked(const QString& path, bool checked) override {} + virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {} + virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {} + virtual void showDisplayPluginsTools() override {} + virtual void requestReset() override {} + virtual QGLWidget* getPrimarySurface() override { return nullptr; } + virtual bool isForeground() override { return true; } + virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } +}; + +class MyControllerScriptingInterface : public controller::ScriptingInterface { +public: + virtual void registerControllerTypes(QScriptEngine* engine) {}; +}; + + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); QQmlApplicationEngine engine; auto rootContext = engine.rootContext(); new PluginContainerProxy(); - - // Simulate our application idle loop - QTimer timer; - QObject::connect(&timer, &QTimer::timeout, [] { - static float last = secTimestampNow(); - float now = secTimestampNow(); - float delta = now - last; - last = now; - - foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - inputPlugin->pluginUpdate(delta, false); - } - - auto userInputMapper = DependencyManager::get(); - userInputMapper->update(delta); - }); - timer.start(50); - { - DependencyManager::set(); - foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - QString name = inputPlugin->getName(); - inputPlugin->activate(); - auto userInputMapper = DependencyManager::get(); - if (name == KeyboardMouseDevice::NAME) { - auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky - keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); - } - inputPlugin->pluginUpdate(0, false); - } + // Simulate our application idle loop + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, [] { + static float last = secTimestampNow(); + float now = secTimestampNow(); + float delta = now - last; + last = now; + + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + inputPlugin->pluginUpdate(delta, false); + } + + auto userInputMapper = DependencyManager::get(); + userInputMapper->update(delta); + }); + timer.start(50); + + { + DependencyManager::set(); + foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { + QString name = inputPlugin->getName(); + inputPlugin->activate(); + auto userInputMapper = DependencyManager::get(); + if (name == KeyboardMouseDevice::NAME) { + auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky + keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); + } + inputPlugin->pluginUpdate(0, false); + } rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); - } - qDebug() << getQmlDir(); + } + qDebug() << getQmlDir(); rootContext->setContextProperty("ResourcePath", getQmlDir()); - engine.setBaseUrl(QUrl::fromLocalFile(getQmlDir())); - engine.addImportPath(getQmlDir()); - engine.load(getTestQmlDir() + "main.qml"); - for (auto pathItem : engine.importPathList()) { - qDebug() << pathItem; - } - app.exec(); - return 0; -} - -#include "main.moc" - + engine.setBaseUrl(QUrl::fromLocalFile(getQmlDir())); + engine.addImportPath(getQmlDir()); + engine.load(getTestQmlDir() + "main.qml"); + for (auto pathItem : engine.importPathList()) { + qDebug() << pathItem; + } + app.exec(); + return 0; +} + +#include "main.moc" + From 7669f9ed2c82d26b7231de7b861099fa22a8212d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 17:26:57 -0700 Subject: [PATCH 064/232] fix several warnings --- interface/src/scripting/ControllerScriptingInterface.cpp | 2 +- libraries/controllers/src/controllers/Filter.cpp | 4 ++-- libraries/controllers/src/controllers/ScriptingInterface.cpp | 5 ++--- .../controllers/src/controllers/impl/RouteBuilderProxy.h | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 1e35713e16..f05d1a41a6 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -154,7 +154,7 @@ controller::InputController::Pointer ControllerScriptingInterface::createInputCo if (trackerID >= 0) { controller::InputController::Pointer inputController = std::make_shared(deviceID, trackerID, this); controller::InputController::Key key = inputController->getKey(); - _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController)); + _inputControllers.insert(InputControllerMap::value_type(key, inputController)); return inputController; } } diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index 43317fd62d..c9ff24fa78 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -89,9 +89,9 @@ PulseFilter::FactoryEntryBuilder PulseFilter::_factoryEntryBuilder; float PulseFilter::apply(float value) const { - float result = 0.0; + float result = 0.0f; - if (0.0 != value) { + if (0.0f != value) { float now = secTimestampNow(); float delta = now - _lastEmitTime; if (delta >= _interval) { diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 3e470fd365..dc305c4c6e 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -96,7 +96,7 @@ namespace controller { : Endpoint(UserInputMapper::Input(UserInputMapper::Input::INVALID_INPUT)), Pair(first, second) { } virtual float value() { - float result = first->value() * -1.0 + second->value(); + float result = first->value() * -1.0f + second->value(); return result; } @@ -324,7 +324,6 @@ namespace controller { float value = getValue(source); // Apply each of the filters. - const auto& filters = route->_filters; for (const auto& filter : route->_filters) { value = filter->apply(value); } @@ -406,7 +405,7 @@ namespace controller { } bool ScriptingInterface::isButtonPressed(int buttonIndex) const { - return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true; + return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0f ? false : true; } int ScriptingInterface::getNumberOfTriggers() const { diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 8e3b3404cc..66b5e85394 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -52,10 +52,9 @@ class RouteBuilderProxy : public QObject { void to(const Endpoint::Pointer& destination); void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); + ScriptingInterface& _parent; Mapping::Pointer _mapping; Route::Pointer _route; - - ScriptingInterface& _parent; }; } From 0068af4cb6f10849ad5d6d3286d90b602d308321 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 17:31:42 -0700 Subject: [PATCH 065/232] fix several warnings --- .../controllers/src/controllers/Filter.cpp | 2 +- .../controllers/src/controllers/Route.cpp | 29 +------------------ 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index c9ff24fa78..94fb87c48e 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -72,7 +72,7 @@ DeadZoneFilter::FactoryEntryBuilder DeadZoneFilter::_factoryEntryBuilder; float DeadZoneFilter::apply(float value) const { float scale = 1.0f / (1.0f - _min); - if (abs(value) < _min) { + if (std::abs(value) < _min) { return 0.0f; } return (value - _min) * scale; diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/Route.cpp index b9116d2813..08b6d1f4f2 100644 --- a/libraries/controllers/src/controllers/Route.cpp +++ b/libraries/controllers/src/controllers/Route.cpp @@ -6,32 +6,5 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#pragma once -#ifndef hifi_Controllers_Route_h -#define hifi_Controllers_Route_h -#include "Endpoint.h" -#include "Filter.h" -#include "Logging.h" - -class QJsonObject; - -namespace controller { - - /* - * encapsulates a source, destination and filters to apply - */ - class Route { - public: - Endpoint::Pointer _source; - Endpoint::Pointer _destination; - Filter::List _filters; - - using Pointer = std::shared_ptr; - using List = std::list; - - void parse(const QJsonObject& json); - }; -} - -#endif +#include "Route.h" From 9acff9497ca3c9e0ad2311590a097736ef59e70f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 17:34:59 -0700 Subject: [PATCH 066/232] fix several warnings --- libraries/controllers/src/controllers/StandardController.cpp | 2 -- libraries/input-plugins/src/input-plugins/SixenseManager.cpp | 4 ---- 2 files changed, 6 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 6b1ada25ed..5c13c66a07 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -15,8 +15,6 @@ #include "StandardController.h" -const float CONTROLLER_THRESHOLD = 0.3f; - StandardController::~StandardController() { } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index e8cebd8e54..b304f260b3 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -32,10 +32,6 @@ Q_DECLARE_LOGGING_CATEGORY(inputplugins) Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") -// These bits aren't used for buttons, so they can be used as masks: -const unsigned int LEFT_MASK = 0; -const unsigned int RIGHT_MASK = 1U << 1; - #ifdef HAVE_SIXENSE const int CALIBRATION_STATE_IDLE = 0; From 8701d73ee957f39c6d8e27e98b9a6b47198ac545 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 19 Oct 2015 19:05:37 -0700 Subject: [PATCH 067/232] DOing the groundwork to pass along the pose still not working --- examples/controllers/controllerMappings.js | 178 +++++++++--------- .../controllers/src/controllers/Endpoint.h | 7 +- .../src/controllers/ScriptingInterface.cpp | 121 ++++++++++-- .../src/controllers/ScriptingInterface.h | 3 + .../src/controllers/StandardController.cpp | 4 +- .../src/controllers/StandardControls.h | 4 +- .../src/controllers/UserInputMapper.cpp | 36 +++- .../src/controllers/UserInputMapper.h | 7 +- .../src/input-plugins/SixenseManager.cpp | 8 +- 9 files changed, 254 insertions(+), 114 deletions(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index 4de173f16c..66efa63676 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -1,90 +1,90 @@ - -// -// controllerScriptingExamples.js -// examples -// -// Created by Sam Gondelman on 6/2/15 -// 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 -// - -// Assumes you only have the default keyboard connected - -/*myFirstMapping = function() { -return { - "name": "example", - "channels": [ - { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, - - { "from": "Keyboard.Left", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.Right", "to": "Actions.LATERAL_RIGHT" }, - - { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, - - { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, - { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, - { - "from": "Standard.LX", - "filters": [ { - "type": "scale", - "params": [2.0], - } - ], - "to": "Actions.LATERAL_LEFT", - }, { - "from": "Keyboard.B", - "to": "Actions.Yaw" - } - ] -} -} -*/ -mySecondMapping = function() { -return { - "name": "example2", - "channels": [ - { "from": "Standard.LY", "to": "Actions.TranslateZ" }, - { "from": "Standard.LX", "to": "Actions.Yaw" }, - ] -} -} - -//Script.include('mapping-test0.json'); -/*var myFirstMappingJSON = myFirstMapping(); -print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); - -var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); - - -Controller.enableMapping("example3"); - -var mySecondMappingJSON = mySecondMapping(); -print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); - -var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); -mapping2.enable(); - -Controller.enableMapping("example2"); -*/ -var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json")); -Controller.enableMapping("example3"); - -/* -Object.keys(Controller.Standard).forEach(function (input) { - print("Controller.Standard." + input + ":" + Controller.Standard[input]); -}); - -Object.keys(Controller.Hardware).forEach(function (deviceName) { - Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { - print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); - }); -}); - -Object.keys(Controller.Actions).forEach(function (actionName) { - print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]); -}); + +// +// controllerScriptingExamples.js +// examples +// +// Created by Sam Gondelman on 6/2/15 +// 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 +// + +// Assumes you only have the default keyboard connected + +/*myFirstMapping = function() { +return { + "name": "example", + "channels": [ + { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, + + { "from": "Keyboard.Left", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "to": "Actions.LATERAL_RIGHT" }, + + { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, + { + "from": "Standard.LX", + "filters": [ { + "type": "scale", + "params": [2.0], + } + ], + "to": "Actions.LATERAL_LEFT", + }, { + "from": "Keyboard.B", + "to": "Actions.Yaw" + } + ] +} +} +*/ +mySecondMapping = function() { +return { + "name": "example2", + "channels": [ + { "from": "Standard.LY", "to": "Actions.TranslateZ" }, + { "from": "Standard.LX", "to": "Actions.Yaw" }, + ] +} +} + +//Script.include('mapping-test0.json'); +/*var myFirstMappingJSON = myFirstMapping(); +print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); + +var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); + + +Controller.enableMapping("example3"); + +var mySecondMappingJSON = mySecondMapping(); +print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); + +var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); +mapping2.enable(); + +Controller.enableMapping("example2"); +*/ +var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json")); +Controller.enableMapping("example3"); + +/* +Object.keys(Controller.Standard).forEach(function (input) { + print("Controller.Standard." + input + ":" + Controller.Standard[input]); +}); + +Object.keys(Controller.Hardware).forEach(function (deviceName) { + Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { + print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); + }); +}); + +Object.keys(Controller.Actions).forEach(function (actionName) { + print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]); +}); */ \ No newline at end of file diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 923412ac6c..ee549b3af8 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -35,7 +35,12 @@ namespace controller { Endpoint(const UserInputMapper::Input& input) : _input(input) {} virtual float value() = 0; virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; - const UserInputMapper::Input& getInput() { return _input; } + virtual Pose pose() { return Pose(); } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {} + + virtual const bool isPose() { return _input.isPose(); } + + const UserInputMapper::Input& getInput() { return _input; } protected: UserInputMapper::Input _input; }; diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 3e470fd365..1c753cf750 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -35,10 +35,16 @@ namespace controller { } virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } - + virtual void apply(float newValue, float oldValue, const Pointer& source) override { + _currentValue = newValue; + } + virtual Pose pose() override { return _currentPose; } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { + _currentPose = newValue; + } private: float _currentValue{ 0.0f }; + Pose _currentPose{}; }; @@ -109,6 +115,49 @@ namespace controller { Endpoint::Pointer _second; }; + class InputEndpoint : public Endpoint { + public: + InputEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT) + : Endpoint(id) { + } + + virtual float value() override { + _currentValue = 0.0f; + if (isPose()) { + return _currentValue; + } + auto userInputMapper = DependencyManager::get(); + auto deviceProxy = userInputMapper->getDeviceProxy(_input); + if (!deviceProxy) { + return _currentValue; + } + _currentValue = deviceProxy->getValue(_input, 0); + return _currentValue; + } + virtual void apply(float newValue, float oldValue, const Pointer& source) override {} + + virtual Pose pose() override { + _currentPose = Pose(); + if (!isPose()) { + return _currentPose; + } + auto userInputMapper = DependencyManager::get(); + auto deviceProxy = userInputMapper->getDeviceProxy(_input); + if (!deviceProxy) { + return _currentPose; + } + _currentPose = deviceProxy->getPose(_input, 0); + return _currentPose; + } + + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { + } + + private: + float _currentValue{ 0.0f }; + Pose _currentPose{}; + }; + class ActionEndpoint : public Endpoint { public: ActionEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT) @@ -125,8 +174,18 @@ namespace controller { } } + virtual Pose pose() override { return _currentPose; } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { + _currentPose = newValue; + if (!(_input == UserInputMapper::Input::INVALID_INPUT)) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->setActionState(UserInputMapper::Action(_input.getChannel()), _currentPose); + } + } + private: float _currentValue{ 0.0f }; + Pose _currentPose{}; }; QRegularExpression ScriptingInterface::SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; @@ -263,8 +322,34 @@ namespace controller { return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::AXIS).getID()); } + + Pose ScriptingInterface::getPoseValue(const int& source) const { + if (!Input(source).isPose()) { + return Pose(); + } + + UserInputMapper::Input input(source); + auto iterator = _endpoints.find(input); + if (_endpoints.end() == iterator) { + return Pose(); + } + + const auto& endpoint = iterator->second; + return getPoseValue(endpoint); + } + + Pose ScriptingInterface::getPoseValue(const Endpoint::Pointer& endpoint) const { + + /*auto valuesIterator = _overrideValues.find(endpoint); + if (_overrideValues.end() != valuesIterator) { + return valuesIterator->second; + }*/ + + return endpoint->pose(); + } + Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { - return Pose(); + return getPoseValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::POSE).getID()); } void ScriptingInterface::update() { @@ -321,18 +406,25 @@ namespace controller { } // Fetch the value, may have been overriden by previous loopback routes - float value = getValue(source); + if (source->isPose()) { + Pose value = getPoseValue(source); - // Apply each of the filters. - const auto& filters = route->_filters; - for (const auto& filter : route->_filters) { - value = filter->apply(value); - } - if (loopback) { - _overrideValues[source] = value; + destination->apply(value, Pose(), source); } else { - destination->apply(value, 0, source); + float value = getValue(source); + + // Apply each of the filters. + const auto& filters = route->_filters; + for (const auto& filter : route->_filters) { + value = filter->apply(value); + } + + if (loopback) { + _overrideValues[source] = value; + } else { + destination->apply(value, 0, source); + } } } } @@ -483,13 +575,14 @@ namespace controller { if (_endpoints.count(input)) { continue; } - _endpoints[input] = std::make_shared([=] { + /* _endpoints[input] = std::make_shared([=] { auto deviceProxy = userInputMapper->getDeviceProxy(input); if (!deviceProxy) { return 0.0f; } return deviceProxy->getValue(input, 0); - }); + });*/ + _endpoints[input] = std::make_shared(input); } } } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index f473562b9e..ac3cd6b6c8 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -79,7 +79,9 @@ namespace controller { Q_INVOKABLE float getValue(const int& source) const; Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const; Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const; + Q_INVOKABLE Pose getPoseValue(const int& source) const; Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; + Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); } @@ -148,6 +150,7 @@ namespace controller { void update(Mapping::Pointer& mapping, EndpointSet& consumed); float getValue(const Endpoint::Pointer& endpoint) const; + Pose getPoseValue(const Endpoint::Pointer& endpoint) const; Endpoint::Pointer endpointFor(const QJSValue& endpoint); Endpoint::Pointer endpointFor(const QScriptValue& endpoint); Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 6b1ada25ed..c3261bc789 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -72,8 +72,8 @@ void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); // Poses - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT), "LeftPose")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT), "RightPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT_HAND), "LeftHand")); + availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT_HAND), "RightHand")); // Aliases, PlayStation style names availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index 0990e34224..dc39a8bbeb 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -50,8 +50,8 @@ namespace controller { // No correlation to SDL enum StandardPoseChannel { - LEFT = 0, - RIGHT, + LEFT_HAND = 0, + RIGHT_HAND, HEAD, NUM_STANDARD_POSES }; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 26e03b7719..520d19f83f 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -308,12 +308,17 @@ void UserInputMapper::update(float deltaTime) { } } - // Scale all the channel step with the scale + //manage the external action states changes coming from the Controllers Graph for (auto i = 0; i < NUM_ACTIONS; i++) { if (_externalActionStates[i] != 0) { _actionStates[i] += _externalActionStates[i]; _externalActionStates[i] = 0.0f; } + + if (_externalPoseStates[i].isValid()) { + _poseStates[i] = _externalPoseStates[i]; + _externalPoseStates[i] = PoseValue(); + } } // merge the bisected and non-bisected axes for now @@ -432,6 +437,35 @@ void UserInputMapper::createActionNames() { _actionNames[ROLL] = "Roll"; _actionNames[PITCH] = "Pitch"; _actionNames[YAW] = "Yaw"; + + _actionInputs[LONGITUDINAL_BACKWARD] = Input(ACTIONS_DEVICE, LONGITUDINAL_BACKWARD, ChannelType::AXIS); + _actionInputs[LONGITUDINAL_FORWARD] = Input(ACTIONS_DEVICE, LONGITUDINAL_BACKWARD, ChannelType::AXIS); + _actionInputs[LATERAL_LEFT] = Input(ACTIONS_DEVICE, LATERAL_LEFT, ChannelType::AXIS); + _actionInputs[LATERAL_RIGHT] = Input(ACTIONS_DEVICE, LATERAL_RIGHT, ChannelType::AXIS); + _actionInputs[VERTICAL_DOWN] = Input(ACTIONS_DEVICE, VERTICAL_DOWN, ChannelType::AXIS); + _actionInputs[VERTICAL_UP] = Input(ACTIONS_DEVICE, VERTICAL_UP, ChannelType::AXIS); + _actionInputs[YAW_LEFT] = Input(ACTIONS_DEVICE, YAW_LEFT, ChannelType::AXIS); + _actionInputs[YAW_RIGHT] = Input(ACTIONS_DEVICE, YAW_RIGHT, ChannelType::AXIS); + _actionInputs[PITCH_DOWN] = Input(ACTIONS_DEVICE, PITCH_DOWN, ChannelType::AXIS); + _actionInputs[PITCH_UP] = Input(ACTIONS_DEVICE, PITCH_UP, ChannelType::AXIS); + _actionInputs[BOOM_IN] = Input(ACTIONS_DEVICE, BOOM_IN, ChannelType::AXIS); + _actionInputs[BOOM_OUT] = Input(ACTIONS_DEVICE, BOOM_OUT, ChannelType::AXIS); + _actionInputs[LEFT_HAND] = Input(ACTIONS_DEVICE, LEFT_HAND, ChannelType::POSE); + _actionInputs[RIGHT_HAND] = Input(ACTIONS_DEVICE, RIGHT_HAND, ChannelType::POSE); + _actionInputs[LEFT_HAND_CLICK] = Input(ACTIONS_DEVICE, LEFT_HAND_CLICK, ChannelType::AXIS); + _actionInputs[RIGHT_HAND_CLICK] = Input(ACTIONS_DEVICE, RIGHT_HAND_CLICK, ChannelType::AXIS); + _actionInputs[SHIFT] = Input(ACTIONS_DEVICE, SHIFT, ChannelType::BUTTON); + _actionInputs[ACTION1] = Input(ACTIONS_DEVICE, ACTION1, ChannelType::BUTTON); + _actionInputs[ACTION2] = Input(ACTIONS_DEVICE, ACTION2, ChannelType::BUTTON); + _actionInputs[CONTEXT_MENU] = Input(ACTIONS_DEVICE, CONTEXT_MENU, ChannelType::BUTTON); + _actionInputs[TOGGLE_MUTE] = Input(ACTIONS_DEVICE, TOGGLE_MUTE, ChannelType::AXIS); + _actionInputs[TRANSLATE_X] = Input(ACTIONS_DEVICE, TRANSLATE_X, ChannelType::AXIS); + _actionInputs[TRANSLATE_Y] = Input(ACTIONS_DEVICE, TRANSLATE_Y, ChannelType::AXIS); + _actionInputs[TRANSLATE_Z] = Input(ACTIONS_DEVICE, TRANSLATE_Z, ChannelType::AXIS); + _actionInputs[ROLL] = Input(ACTIONS_DEVICE, ROLL, ChannelType::AXIS); + _actionInputs[PITCH] = Input(ACTIONS_DEVICE, PITCH, ChannelType::AXIS); + _actionInputs[YAW] = Input(ACTIONS_DEVICE, YAW, ChannelType::AXIS); + } void UserInputMapper::registerStandardDevice() { diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index d463ed0482..c9a6af3bd8 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -134,13 +134,15 @@ public: QVector getAllActions() const; QString getActionName(Action action) const { return UserInputMapper::_actionNames[(int) action]; } float getActionState(Action action) const { return _actionStates[action]; } - PoseValue getPoseState(Action action) const { return _poseStates[action]; } + const PoseValue& getPoseState(Action action) const { return _poseStates[action]; } int findAction(const QString& actionName) const; QVector getActionNames() const; + Input getActionInput(Action action) const { return _actionInputs[action]; } void assignDefaulActionScales(); void setActionState(Action action, float value) { _externalActionStates[action] = value; } void deltaActionState(Action action, float delta) { _externalActionStates[action] += delta; } + void setActionState(Action action, const PoseValue& value) { _externalPoseStates[action] = value; } // Add input channel to the mapper and check that all the used channels are registered. // Return true if theinput channel is created correctly, false either @@ -224,11 +226,14 @@ protected: typedef std::multimap ActionToInputsMap; ActionToInputsMap _actionToInputsMap; + std::vector _actionInputs = std::vector(NUM_ACTIONS, Input()); + std::vector _actionStates = std::vector(NUM_ACTIONS, 0.0f); std::vector _externalActionStates = std::vector(NUM_ACTIONS, 0.0f); std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); std::vector _lastActionStates = std::vector(NUM_ACTIONS, 0.0f); std::vector _poseStates = std::vector(NUM_ACTIONS); + std::vector _externalPoseStates = std::vector(NUM_ACTIONS); glm::mat4 _sensorToWorldMat; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index e8cebd8e54..845c51019e 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -236,7 +236,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { _poseStateMap.clear(); } } else { - _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = UserInputMapper::PoseValue(); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND] = UserInputMapper::PoseValue(); } } @@ -444,7 +444,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, boo // TODO: find a shortcut with fewer rotations. rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; - _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = + _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND] = UserInputMapper::PoseValue(position, rotation); #endif // HAVE_SIXENSE } @@ -490,8 +490,8 @@ void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX")); availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY")); availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT), "LeftPose")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT), "RightPose")); + availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "LeftHand")); + availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "RightHand")); return availableInputs; }; mapper.registerDevice(_deviceID, proxy); From 2213a4bb021962ae38aa90f77d2dc0311aed2972 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 19 Oct 2015 20:09:48 -0700 Subject: [PATCH 068/232] Do not set (just rightHand) anim var if a script has done so. --- libraries/animation/src/Rig.cpp | 8 +++++--- libraries/animation/src/Rig.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 05be18b4cc..9737e8ded5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -621,8 +621,8 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { QScriptValue outboundMap = _animVars.animVariantMapToScriptValue(engine); QScriptValueList args; args << outboundMap; - QScriptValue inboundMap = _stateHandlers.call(QScriptValue(), args); - _animVars.animVariantMapFromScriptValue(inboundMap); + _stateHandlersResults = _stateHandlers.call(QScriptValue(), args); + _animVars.animVariantMapFromScriptValue(_stateHandlersResults); //qCDebug(animation) << _animVars.lookup("foo", QString("not set")); } // evaluate the animation @@ -1201,7 +1201,9 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { - _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); + if (!_stateHandlersResults.property("rightHandPosition", QScriptValue::ResolveLocal).isValid()) { + _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); + } _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 69eedc2155..f13de90cba 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -246,6 +246,7 @@ public: private: QScriptValue _stateHandlers {}; + QScriptValue _stateHandlersResults {}; }; #endif /* defined(__hifi__Rig__) */ From ef6c4f6f665c56bd37ae31dede4241a424a95762 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 19:31:25 -0700 Subject: [PATCH 069/232] Working on JSON parsing and cleanup Conflicts: libraries/controllers/src/controllers/Route.cpp libraries/controllers/src/controllers/ScriptingInterface.cpp libraries/controllers/src/controllers/StandardController.cpp libraries/controllers/src/controllers/impl/RouteBuilderProxy.h --- .../resources/controllers/mapping-config.json | 48 ++-- .../resources/controllers/mapping-test0.json | 70 +++--- interface/resources/qml/controller/Xbox.qml | 208 +++++++++--------- 3 files changed, 163 insertions(+), 163 deletions(-) diff --git a/interface/resources/controllers/mapping-config.json b/interface/resources/controllers/mapping-config.json index dd3bc7b05e..2ccd216c2f 100644 --- a/interface/resources/controllers/mapping-config.json +++ b/interface/resources/controllers/mapping-config.json @@ -1,24 +1,24 @@ -{ - "name": "Full Mapping config including the standard hydra and gamepad and one more thing", - "mappings": [ - { "src": "./mapping-hydra.json" }, - { "src": "./mapping-xbox.json" }, - { - "name": "example mapping for standard to js function", - "channels": [ { - "from": "Standard.B", - "to": { - "type":"js", - "function": "function(value){ print(\"Standard.B = \" + value );}" - } - }, { - "from": "Standard.B", - "to": { - "type":"js", - "src": "http://www.theNextBigThing.com/hifiInputSignalHandler.js" - } - } - ] - } - ] -} +{ + "name": "Full Mapping config including the standard hydra and gamepad and one more thing", + "mappings": [ + { "src": "./mapping-hydra.json" }, + { "src": "./mapping-xbox.json" }, + { + "name": "example mapping for standard to js function", + "channels": [ { + "from": "Standard.B", + "to": { + "type":"js", + "function": "function(value){ print(\"Standard.B = \" + value );}" + } + }, { + "from": "Standard.B", + "to": { + "type":"js", + "src": "http://www.theNextBigThing.com/hifiInputSignalHandler.js" + } + } + ] + } + ] +} diff --git a/interface/resources/controllers/mapping-test0.json b/interface/resources/controllers/mapping-test0.json index d6a1de5313..5232c97f19 100644 --- a/interface/resources/controllers/mapping-test0.json +++ b/interface/resources/controllers/mapping-test0.json @@ -1,36 +1,36 @@ -{ - "name": "example mapping from Standard to actions", - "channels": [ { - "from": "Standard.LY", - "filters": [ { - "type": "clamp", - "min": 0, - "max": 1 - } - ], - "to": "Actions.Forward" - }, { - "from": "Standard.LY", - "filters": [ { - "type": "clamp", - "min": -1, - "max": 0 - }, { - "type": "invert" - } - ], - "to": "Actions.Backward" - }, { - "from": "Standard.LX", - "filters": [ { - "type": "scale", - "scale": 2.0 - } - ], - "to": "Actions.Yaw" - }, { - "from": "Standard.A", - "to": "Actions.Action0" - } - ] +{ + "name": "example mapping from Standard to actions", + "channels": [ { + "from": "Standard.LY", + "filters": [ { + "type": "clamp", + "min": 0, + "max": 1 + } + ], + "to": "Actions.Forward" + }, { + "from": "Standard.LY", + "filters": [ { + "type": "clamp", + "min": -1, + "max": 0 + }, { + "type": "invert" + } + ], + "to": "Actions.Backward" + }, { + "from": "Standard.LX", + "filters": [ { + "type": "scale", + "scale": 2.0 + } + ], + "to": "Actions.Yaw" + }, { + "from": "Standard.A", + "to": "Actions.Action0" + } + ] } \ No newline at end of file diff --git a/interface/resources/qml/controller/Xbox.qml b/interface/resources/qml/controller/Xbox.qml index 165ac596fe..4ff2959129 100644 --- a/interface/resources/qml/controller/Xbox.qml +++ b/interface/resources/qml/controller/Xbox.qml @@ -1,104 +1,104 @@ -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Dialogs 1.0 - -import "xbox" - -Item { - id: root - property real aspect: 300.0 / 215.0 - width: 300 - height: width / aspect - property var device - property string label: "" - property real scale: width / 300.0 - - Image { - Text { - anchors.left: parent.left - anchors.top: parent.top - text: root.label - visible: root.label != "" - } - anchors.fill: parent - source: "xbox/xbox360-controller-md.png" - - LeftAnalogStick { - device: root.device - x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 - } - - // Left stick press - ToggleButton { - controlId: root.device.LS - width: 16 * root.scale; height: 16 * root.scale - x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 - } - - - RightAnalogStick { - device: root.device - x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 - } - - // Right stick press - ToggleButton { - controlId: root.device.RS - width: 16 * root.scale; height: 16 * root.scale - x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 - } - - // Left trigger - AnalogButton { - controlId: root.device.LT - width: 8; height: 64 - x: (20 * root.scale); y: (7 * root.scale) - } - - // Right trigger - AnalogButton { - controlId: root.device.RT - width: 8; height: 64 - x: (272 * root.scale); y: (7 * root.scale) - } - - // Left bumper - ToggleButton { - controlId: root.device.LB - width: 32 * root.scale; height: 16 * root.scale - x: (40 * root.scale); y: (7 * root.scale) - } - - // Right bumper - ToggleButton { - controlId: root.device.RB - width: 32 * root.scale; height: 16 * root.scale - x: (root.width - width) - (40 * root.scale); y: (7 * root.scale) - } - - DPad { - device: root.device - size: 48 * root.scale - x: (80 * root.scale); y: (71 * root.scale) - } - - XboxButtons { - device: root.device - size: 65 * root.scale - x: (206 * root.scale); y: (19 * root.scale) - } - - ToggleButton { - controlId: root.device.Back - width: 16 * root.scale; height: 12 * root.scale - x: (112 * root.scale); y: (45 * root.scale) - } - - ToggleButton { - controlId: root.device.Start - width: 16 * root.scale; height: 12 * root.scale - x: (177 * root.scale); y: (45 * root.scale) - } - } -} +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "xbox" + +Item { + id: root + property real aspect: 300.0 / 215.0 + width: 300 + height: width / aspect + property var device + property string label: "" + property real scale: width / 300.0 + + Image { + Text { + anchors.left: parent.left + anchors.top: parent.top + text: root.label + visible: root.label != "" + } + anchors.fill: parent + source: "xbox/xbox360-controller-md.png" + + LeftAnalogStick { + device: root.device + x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 + } + + // Left stick press + ToggleButton { + controlId: root.device.LS + width: 16 * root.scale; height: 16 * root.scale + x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 + } + + + RightAnalogStick { + device: root.device + x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 + } + + // Right stick press + ToggleButton { + controlId: root.device.RS + width: 16 * root.scale; height: 16 * root.scale + x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 + } + + // Left trigger + AnalogButton { + controlId: root.device.LT + width: 8; height: 64 + x: (20 * root.scale); y: (7 * root.scale) + } + + // Right trigger + AnalogButton { + controlId: root.device.RT + width: 8; height: 64 + x: (272 * root.scale); y: (7 * root.scale) + } + + // Left bumper + ToggleButton { + controlId: root.device.LB + width: 32 * root.scale; height: 16 * root.scale + x: (40 * root.scale); y: (7 * root.scale) + } + + // Right bumper + ToggleButton { + controlId: root.device.RB + width: 32 * root.scale; height: 16 * root.scale + x: (root.width - width) - (40 * root.scale); y: (7 * root.scale) + } + + DPad { + device: root.device + size: 48 * root.scale + x: (80 * root.scale); y: (71 * root.scale) + } + + XboxButtons { + device: root.device + size: 65 * root.scale + x: (206 * root.scale); y: (19 * root.scale) + } + + ToggleButton { + controlId: root.device.Back + width: 16 * root.scale; height: 12 * root.scale + x: (112 * root.scale); y: (45 * root.scale) + } + + ToggleButton { + controlId: root.device.Start + width: 16 * root.scale; height: 12 * root.scale + x: (177 * root.scale); y: (45 * root.scale) + } + } +} From bea6fdd8902327ad3e1d820d616a4e20d25692f2 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 19:31:25 -0700 Subject: [PATCH 070/232] Working on JSON parsing and cleanup Conflicts: libraries/controllers/src/controllers/Route.cpp libraries/controllers/src/controllers/ScriptingInterface.cpp libraries/controllers/src/controllers/StandardController.cpp libraries/controllers/src/controllers/impl/RouteBuilderProxy.h --- .../resources/controllers/keyboardMouse.json | 25 + interface/resources/controllers/standard.json | 41 + interface/resources/controllers/xbox.json | 40 +- interface/resources/qml/TestControllers.qml | 2 +- interface/src/Application.cpp | 66 +- interface/src/Application.h | 2 +- interface/src/devices/3DConnexionClient.cpp | 153 ++-- interface/src/devices/3DConnexionClient.h | 28 +- .../ControllerScriptingInterface.cpp | 25 - .../scripting/ControllerScriptingInterface.h | 2 - interface/src/ui/ApplicationCompositor.cpp | 2 +- interface/src/ui/PreferencesDialog.cpp | 4 +- .../controllers/src/controllers/Actions.cpp | 71 ++ .../controllers/src/controllers/Actions.h | 100 +++ .../src/controllers/DeviceProxy.cpp | 2 +- .../controllers/src/controllers/DeviceProxy.h | 24 +- .../controllers/src/controllers/Endpoint.h | 14 +- libraries/controllers/src/controllers/Input.h | 39 +- .../src/controllers/InputDevice.cpp | 107 +-- .../controllers/src/controllers/InputDevice.h | 45 +- .../controllers/src/controllers/Mapping.cpp | 8 - .../controllers/src/controllers/Mapping.h | 8 +- .../controllers/src/controllers/Pose.cpp | 8 +- libraries/controllers/src/controllers/Pose.h | 20 +- .../controllers/src/controllers/Route.cpp | 1 - libraries/controllers/src/controllers/Route.h | 9 +- .../src/controllers/ScriptingInterface.cpp | 620 +++---------- .../src/controllers/ScriptingInterface.h | 83 +- .../src/controllers/StandardController.cpp | 213 ++--- .../src/controllers/StandardController.h | 24 +- .../src/controllers/UserInputMapper.cpp | 830 ++++++++++++------ .../src/controllers/UserInputMapper.h | 351 +++----- .../controllers/impl/MappingBuilderProxy.cpp | 46 +- .../controllers/impl/MappingBuilderProxy.h | 13 +- .../controllers/impl/RouteBuilderProxy.cpp | 40 +- .../src/controllers/impl/RouteBuilderProxy.h | 13 +- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/input-plugins/Joystick.cpp | 190 ++-- .../src/input-plugins/Joystick.h | 10 +- .../src/input-plugins/KeyboardMouseDevice.cpp | 240 +++-- .../src/input-plugins/KeyboardMouseDevice.h | 16 +- .../src/input-plugins/SDL2Manager.cpp | 19 +- .../src/input-plugins/SixenseManager.cpp | 168 ++-- .../src/input-plugins/SixenseManager.h | 6 +- .../input-plugins/ViveControllerManager.cpp | 258 +++--- .../src/input-plugins/ViveControllerManager.h | 39 +- libraries/script-engine/src/ScriptEngine.cpp | 4 +- libraries/script-engine/src/ScriptEngine.h | 2 - tests/controllers/src/main.cpp | 8 +- 49 files changed, 1925 insertions(+), 2116 deletions(-) create mode 100644 interface/resources/controllers/keyboardMouse.json create mode 100644 interface/resources/controllers/standard.json create mode 100644 libraries/controllers/src/controllers/Actions.cpp create mode 100644 libraries/controllers/src/controllers/Actions.h diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json new file mode 100644 index 0000000000..71450a0c48 --- /dev/null +++ b/interface/resources/controllers/keyboardMouse.json @@ -0,0 +1,25 @@ +{ + "name": "Keyboard/Mouse to Actions", + "channels": [ + { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, + { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" } + ] +} diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json new file mode 100644 index 0000000000..364d24ae16 --- /dev/null +++ b/interface/resources/controllers/standard.json @@ -0,0 +1,41 @@ +{ + "name": "Standard to Action", + "channels": [ + { "from": "Standard.LY", "to": "Actions.TranslateZ" }, + { "from": "Standard.LX", "to": "Actions.TranslateX" }, + { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RY", "to": "Actions.Pitch" }, + { + "from": "Standard.DU", + "to": "Actions.LONGITUDINAL_FORWARD", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { + "from": "Standard.DD", + "to": "Actions.LONGITUDINAL_BACKWARD", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { + "from": "Standard.DR", + "to": "Actions.LATERAL_RIGHT", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { + "from": "Standard.DL", + "to": "Actions.LATERAL_LEFT", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { "from": "Standard.Y", "to": "Actions.VERTICAL_UP" }, + { "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" }, + { + "from": "Standard.RT", + "to": "Actions.BOOM_IN", + "filters": [ { "type": "scale", "scale": 0.1 } ] + }, + { + "from": "Standard.LT", + "to": "Actions.BOOM_OUT", + "filters": [ { "type": "scale", "scale": 0.1 } ] + } + ] +} diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json index bf96320707..8c341dff83 100644 --- a/interface/resources/controllers/xbox.json +++ b/interface/resources/controllers/xbox.json @@ -1,29 +1,29 @@ { "name": "XBox to Standard", "channels": [ - { "from": "XBox.LY", "to": "Standard.LY" }, - { "from": "XBox.LX", "to": "Standard.LX" }, - { "from": "XBox.LT", "to": "Standard.LT" }, - { "from": "XBox.LB", "to": "Standard.LB" }, - { "from": "XBox.LS", "to": "Standard.LS" }, + { "from": "GamePad.LY", "to": "Standard.LY" }, + { "from": "GamePad.LX", "to": "Standard.LX" }, + { "from": "GamePad.LT", "to": "Standard.LT" }, + { "from": "GamePad.LB", "to": "Standard.LB" }, + { "from": "GamePad.LS", "to": "Standard.LS" }, - { "from": "XBox.RY", "to": "Standard.RY" }, - { "from": "XBox.RX", "to": "Standard.RX" }, - { "from": "XBox.RT", "to": "Standard.RT" }, - { "from": "XBox.RB", "to": "Standard.RB" }, - { "from": "XBox.RS", "to": "Standard.RS" }, + { "from": "GamePad.RY", "to": "Standard.RY" }, + { "from": "GamePad.RX", "to": "Standard.RX" }, + { "from": "GamePad.RT", "to": "Standard.RT" }, + { "from": "GamePad.RB", "to": "Standard.RB" }, + { "from": "GamePad.RS", "to": "Standard.RS" }, - { "from": "XBox.Back", "to": "Standard.Back" }, - { "from": "XBox.Start", "to": "Standard.Start" }, + { "from": "GamePad.Back", "to": "Standard.Back" }, + { "from": "GamePad.Start", "to": "Standard.Start" }, - { "from": "XBox.DU", "to": "Standard.DU" }, - { "from": "XBox.DD", "to": "Standard.DD" }, - { "from": "XBox.DL", "to": "Standard.DL" }, - { "from": "XBox.DR", "to": "Standard.DR" }, + { "from": "GamePad.DU", "to": "Standard.DU" }, + { "from": "GamePad.DD", "to": "Standard.DD" }, + { "from": "GamePad.DL", "to": "Standard.DL" }, + { "from": "GamePad.DR", "to": "Standard.DR" }, - { "from": "XBox.A", "to": "Standard.A" }, - { "from": "XBox.B", "to": "Standard.B" }, - { "from": "XBox.X", "to": "Standard.X" }, - { "from": "XBox.Y", "to": "Standard.Y" } + { "from": "GamePad.A", "to": "Standard.A" }, + { "from": "GamePad.B", "to": "Standard.B" }, + { "from": "GamePad.X", "to": "Standard.X" }, + { "from": "GamePad.Y", "to": "Standard.Y" } ] } diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index 79be877aa3..e409b7a4a4 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -25,7 +25,7 @@ HifiControls.VrDialog { Component.onCompleted: { enabled = true - var xboxRegex = /^X360Controller/; + var xboxRegex = /^GamePad/; var hydraRegex = /^Hydra/; for (var prop in Controller.Hardware) { if(xboxRegex.test(prop)) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 77140fc0d3..c3eb3dfe7c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -631,7 +631,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { if (state) { switch (action) { - case UserInputMapper::Action::TOGGLE_MUTE: + case controller::Action::TOGGLE_MUTE: DependencyManager::get()->toggleMute(); break; } @@ -639,8 +639,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : }); // Setup the keyboardMouseDevice and the user input mapper with the default bindings - _keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); - _keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper); + userInputMapper->registerDevice(_keyboardMouseDevice); // check first run... if (_firstRun.get()) { @@ -2182,7 +2181,7 @@ float Application::getAvatarSimrate() { } void Application::setLowVelocityFilter(bool lowVelocityFilter) { - InputDevice::setLowVelocityFilter(lowVelocityFilter); + controller::InputDevice::setLowVelocityFilter(lowVelocityFilter); } ivec2 Application::getMouse() const { @@ -2710,19 +2709,28 @@ void Application::update(float deltaTime) { auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); - // Dispatch input events - _controllerScriptingInterface->update(); + userInputMapper->update(deltaTime); + + bool jointsCaptured = false; + for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { + if (inputPlugin->isActive()) { + inputPlugin->pluginUpdate(deltaTime, jointsCaptured); + if (inputPlugin->isJointController()) { + jointsCaptured = true; + } + } + } // Transfer the user inputs to the driveKeys myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { if (!_controllerScriptingInterface->areActionsCaptured()) { - myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); - myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); - myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP)); - myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN)); - myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT)); - myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT)); + myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(controller::LONGITUDINAL_FORWARD)); + myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(controller::LONGITUDINAL_BACKWARD)); + myAvatar->setDriveKeys(UP, userInputMapper->getActionState(controller::VERTICAL_UP)); + myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(controller::VERTICAL_DOWN)); + myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(controller::LATERAL_LEFT)); + myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(controller::LATERAL_RIGHT)); if (deltaTime > FLT_EPSILON) { // For rotations what we really want are meausures of "angles per second" (in order to prevent // fps-dependent spin rates) so we need to scale the units of the controller contribution. @@ -2730,25 +2738,25 @@ void Application::update(float deltaTime) { // controllers to provide a delta_per_second value rather than a raw delta.) const float EXPECTED_FRAME_RATE = 60.0f; float timeFactor = EXPECTED_FRAME_RATE * deltaTime; - myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor); - myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor); - myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor); - myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor); + myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(controller::PITCH_UP) / timeFactor); + myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(controller::PITCH_DOWN) / timeFactor); + myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(controller::YAW_LEFT) / timeFactor); + myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(controller::YAW_RIGHT) / timeFactor); } } - myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN)); - myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT)); + myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(controller::BOOM_IN)); + myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(controller::BOOM_OUT)); } - UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND); - UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND); + controller::Pose leftHand = userInputMapper->getPoseState(controller::LEFT_HAND); + controller::Pose rightHand = userInputMapper->getPoseState(controller::RIGHT_HAND); Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); - setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK)); - setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK)); + setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(controller::LEFT_HAND_CLICK)); + setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(controller::RIGHT_HAND_CLICK)); if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { - emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK), - userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX); - emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK), - userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX); + emulateMouse(hand, userInputMapper->getActionState(controller::LEFT_HAND_CLICK), + userInputMapper->getActionState(controller::SHIFT), LEFT_HAND_INDEX); + emulateMouse(hand, userInputMapper->getActionState(controller::RIGHT_HAND_CLICK), + userInputMapper->getActionState(controller::SHIFT), RIGHT_HAND_INDEX); } updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... @@ -4806,7 +4814,7 @@ mat4 Application::getHMDSensorPose() const { return mat4(); } -void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue) { +void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, int index, float triggerValue) { PalmData* palm; bool foundHand = false; for (size_t j = 0; j < hand->getNumPalms(); j++) { @@ -4855,7 +4863,7 @@ void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float palm->setRawAngularVelocity(glm::vec3(0.0f)); } - if (InputDevice::getLowVelocityFilter()) { + if (controller::InputDevice::getLowVelocityFilter()) { // Use a velocity sensitive filter to damp small motions and preserve large ones with // no latency. float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); @@ -4911,7 +4919,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); auto canvasSize = getCanvasSize(); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult(); + float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult(); pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle); pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle); diff --git a/interface/src/Application.h b/interface/src/Application.h index 829265c9fb..75cc418e94 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -352,7 +352,7 @@ private: void update(float deltaTime); - void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue); + void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, int index, float triggerValue); void emulateMouse(Hand* hand, float click, float shift, int index); // Various helper functions called during update() diff --git a/interface/src/devices/3DConnexionClient.cpp b/interface/src/devices/3DConnexionClient.cpp index 722fedcc3a..05795e87e9 100755 --- a/interface/src/devices/3DConnexionClient.cpp +++ b/interface/src/devices/3DConnexionClient.cpp @@ -10,8 +10,11 @@ // #include "3DConnexionClient.h" + +#include +#include + #include "Menu.h" -#include "UserActivityLogger.h" const float MAX_AXIS = 75.0f; // max forward = 2x speed @@ -25,8 +28,9 @@ ConnexionData& ConnexionData::getInstance() { return sharedInstance; } -ConnexionData::ConnexionData() { -} + +ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {} + void ConnexionData::handleAxisEvent() { _axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f; @@ -48,76 +52,71 @@ void ConnexionData::setButton(int lastButtonState) { _buttonPressedMap.insert(lastButtonState); } -void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); +void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { + proxy->_name = _name = "ConnexionClient"; + proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this]() -> QVector { + QVector availableInputs; - auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient")); - proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs; - - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward")); - availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward")); - availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_NEG), "Move Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_POS), "Move up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_NEG), "Move down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward")); - availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward")); - availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down")); + availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_1), "Left button")); + availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_2), "Right button")); + availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_3), "Both buttons")); + availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward")); + availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Y_POS), "Move forward")); + availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_X_POS), "Move right")); + availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_X_NEG), "Move Left")); + availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Z_POS), "Move up")); + availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Z_NEG), "Move down")); + availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward")); + availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward")); + availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right")); + availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left")); + availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up")); + availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down")); return availableInputs; }; - proxy->resetDeviceBindings = [this, &mapper]() -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerDevice(_deviceID, proxy); } -void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - //const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // postion: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED); - - // Rotation: Camera orientation with button 1 - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); - - // Button controls - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED); - - // Zoom - // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED); - // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED); - +QString ConnexionData::getDefaultMappingConfig() { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; + return MAPPING_JSON; } +//void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) { +// const float JOYSTICK_MOVE_SPEED = 1.0f; +// //const float DPAD_MOVE_SPEED = 0.5f; +// const float JOYSTICK_YAW_SPEED = 0.5f; +// const float JOYSTICK_PITCH_SPEED = 0.25f; +// const float BOOM_SPEED = 0.1f; +// +// // Y axes are flipped (up is negative) +// // postion: Movement, strafing +// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED); +// +// // Rotation: Camera orientation with button 1 +// mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED); +// mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED); +// mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); +// mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); +// +// // Button controls +// // Zoom +// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED); +// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED); +// +// // Zoom +// // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED); +// // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED); +// +//} + float ConnexionData::getButton(int channel) const { if (!_buttonPressedMap.empty()) { if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { @@ -138,15 +137,15 @@ float ConnexionData::getAxis(int channel) const { } } -UserInputMapper::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) { + return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); } -UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) { + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -void ConnexionData::update() { +void ConnexionData::update(float deltaTime, bool jointsCaptured) { // the update is done in the ConnexionClient class. // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached @@ -187,7 +186,6 @@ void ConnexionClient::destroy() { QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); ConnexionData& connexiondata = ConnexionData::getInstance(); int deviceid = connexiondata.getDeviceID(); - connexiondata.setDeviceID(0); auto userInputMapper = DependencyManager::get(); userInputMapper->removeDevice(deviceid); } @@ -295,13 +293,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) { ConnexionData& connexiondata = ConnexionData::getInstance(); auto userInputMapper = DependencyManager::get(); if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { - connexiondata.registerToUserInputMapper(*userInputMapper); - connexiondata.assignDefaultInputMapping(*userInputMapper); + userInputMapper->registerDevice(&connexiondata); UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) { - int deviceid = connexiondata.getDeviceID(); - connexiondata.setDeviceID(0); - userInputMapper->removeDevice(deviceid); + userInputMapper->removeDevice(connexiondata.getDeviceID()); } if (!Is3dmouseAttached()) { @@ -894,8 +889,7 @@ void ConnexionClient::init() { if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { auto userInputMapper = DependencyManager::get(); - connexiondata.registerToUserInputMapper(*userInputMapper); - connexiondata.assignDefaultInputMapping(*userInputMapper); + userInputMapper->registerDevice(&connexiondata); UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); } //let one axis be dominant @@ -926,8 +920,7 @@ void DeviceAddedHandler(unsigned int connection) { if (connexiondata.getDeviceID() == 0) { qCWarning(interfaceapp) << "3Dconnexion device added "; auto userInputMapper = DependencyManager::get(); - connexiondata.registerToUserInputMapper(*userInputMapper); - connexiondata.assignDefaultInputMapping(*userInputMapper); + userInputMapper->registerDevice(&connexiondata); UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); } } diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index bddb86a857..8f66a602a4 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -175,13 +175,12 @@ public slots: // connnects to the userinputmapper -class ConnexionData : public QObject { +class ConnexionData : public QObject, public controller::InputDevice { Q_OBJECT public: static ConnexionData& getInstance(); ConnexionData(); - enum PositionChannel { POSITION_AXIS_X_POS = 1, POSITION_AXIS_X_NEG = 2, @@ -209,19 +208,12 @@ public: float getButton(int channel) const; float getAxis(int channel) const; - UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis); - UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button); - - void registerToUserInputMapper(UserInputMapper& mapper); - void assignDefaultInputMapping(UserInputMapper& mapper); - - void update(); - void focusOutEvent(); - - int getDeviceID() { return _deviceID; } - void setDeviceID(int deviceID) { _deviceID = deviceID; } - - QString _name; + controller::Input makeInput(ConnexionData::PositionChannel axis); + controller::Input makeInput(ConnexionData::ButtonChannel button); + virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; glm::vec3 cc_position; glm::vec3 cc_rotation; @@ -229,12 +221,6 @@ public: void setButton(int lastButtonState); void handleAxisEvent(); - -protected: - int _deviceID = 0; - - ButtonPressedMap _buttonPressedMap; - AxisStateMap _axisStateMap; }; #endif // defined(hifi_3DConnexionClient_h) diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index f05d1a41a6..31f823de8b 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -167,31 +167,6 @@ void ControllerScriptingInterface::releaseInputController(controller::InputContr _inputControllers.erase(input->getKey()); } -void ControllerScriptingInterface::update() { - static float last = secTimestampNow(); - float now = secTimestampNow(); - float delta = now - last; - last = now; - - DependencyManager::get()->update(delta); - - bool jointsCaptured = false; - for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { - if (inputPlugin->isActive()) { - inputPlugin->pluginUpdate(delta, jointsCaptured); - if (inputPlugin->isJointController()) { - jointsCaptured = true; - } - } - } - - for (auto entry : _inputControllers) { - entry.second->update(); - } - - controller::ScriptingInterface::update(); -} - InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : _deviceTrackerId(deviceTrackerId), _subTrackerId(subTrackerId), diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 24065e6799..8be530c6ce 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -85,8 +85,6 @@ public: bool isKeyCaptured(const KeyEvent& event) const; bool isJoystickCaptured(int joystickIndex) const; - virtual void update() override; - public slots: virtual void captureKeyEvents(const KeyEvent& event); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 4fc2f3ddb4..ca5c60dc04 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -521,7 +521,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO)); // Get the pixel range over which the xAngle and yAngle are scaled - float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult(); + float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult(); mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 4ba248c76c..d4bab86126 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() { ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); #endif - ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed()); + ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed()); // LOD items auto lodManager = DependencyManager::get(); @@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() { qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); - InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); + controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); auto audio = DependencyManager::get(); MixedProcessedAudioStream& stream = audio->getReceivedAudioStream(); diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp new file mode 100644 index 0000000000..2c5cf84931 --- /dev/null +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -0,0 +1,71 @@ +// +// Created by Bradley Austin Davis on 2015/10/19 +// 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 +// + +#include "Actions.h" + +#include "UserInputMapper.h" + +namespace controller { + + // Device functions + void ActionsDevice::buildDeviceProxy(DeviceProxy::Pointer proxy) { + proxy->_name = _name; + proxy->getButton = [this](const Input& input, int timestamp) -> bool { return false; }; + proxy->getAxis = [this](const Input& input, int timestamp) -> float { return 0; }; + proxy->getAvailabeInputs = [this]() -> QVector { + QVector availableInputs{ + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LONGITUDINAL_BACKWARD, ChannelType::AXIS), "LONGITUDINAL_BACKWARD"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LONGITUDINAL_FORWARD, ChannelType::AXIS), "LONGITUDINAL_FORWARD"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LATERAL_LEFT, ChannelType::AXIS), "LATERAL_LEFT"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LATERAL_RIGHT, ChannelType::AXIS), "LATERAL_RIGHT"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, VERTICAL_DOWN, ChannelType::AXIS), "VERTICAL_DOWN"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, VERTICAL_UP, ChannelType::AXIS), "VERTICAL_UP"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, YAW_LEFT, ChannelType::AXIS), "YAW_LEFT"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, YAW_RIGHT, ChannelType::AXIS), "YAW_RIGHT"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, PITCH_DOWN, ChannelType::AXIS), "PITCH_DOWN"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, PITCH_UP, ChannelType::AXIS), "PITCH_UP"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, BOOM_IN, ChannelType::AXIS), "BOOM_IN"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, BOOM_OUT, ChannelType::AXIS), "BOOM_OUT"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LEFT_HAND, ChannelType::POSE), "LEFT_HAND"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, RIGHT_HAND, ChannelType::POSE), "RIGHT_HAND"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, LEFT_HAND_CLICK, ChannelType::BUTTON), "LEFT_HAND_CLICK"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, RIGHT_HAND_CLICK, ChannelType::BUTTON), "RIGHT_HAND_CLICK"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, SHIFT, ChannelType::BUTTON), "SHIFT"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, ACTION1, ChannelType::BUTTON), "ACTION1"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, ACTION2, ChannelType::BUTTON), "ACTION2"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, CONTEXT_MENU, ChannelType::BUTTON), "CONTEXT_MENU"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TOGGLE_MUTE, ChannelType::AXIS), "TOGGLE_MUTE"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TRANSLATE_X, ChannelType::AXIS), "TranslateX"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TRANSLATE_Y, ChannelType::AXIS), "TranslateY"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, TRANSLATE_Z, ChannelType::AXIS), "TranslateZ"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, ROLL, ChannelType::AXIS), "Roll"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, PITCH, ChannelType::AXIS), "Pitch"), + Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, YAW, ChannelType::AXIS), "Yaw") + }; + return availableInputs; + }; + + } + + QString ActionsDevice::getDefaultMappingConfig() { + return QString(); + } + + void ActionsDevice::update(float deltaTime, bool jointsCaptured) { + } + + void ActionsDevice::focusOutEvent() { + } + + ActionsDevice::ActionsDevice() : InputDevice("Actions") { + _deviceID = UserInputMapper::ACTIONS_DEVICE; + } + + ActionsDevice::~ActionsDevice() {} + +} diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h new file mode 100644 index 0000000000..f4e9f665e2 --- /dev/null +++ b/libraries/controllers/src/controllers/Actions.h @@ -0,0 +1,100 @@ +// +// Created by Bradley Austin Davis on 2015/10/19 +// 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_controller_Actions_h +#define hifi_controller_Actions_h + +#include +#include + +#include "InputDevice.h" + +namespace controller { + +// Actions are the output channels of the Mapper, that's what the InputChannel map to +// For now the Actions are hardcoded, this is bad, but we will fix that in the near future +enum Action { + TRANSLATE_X = 0, + TRANSLATE_Y, + TRANSLATE_Z, + ROTATE_X, PITCH = ROTATE_X, + ROTATE_Y, YAW = ROTATE_Y, + ROTATE_Z, ROLL = ROTATE_Z, + + TRANSLATE_CAMERA_Z, + NUM_COMBINED_AXES, + + LEFT_HAND = NUM_COMBINED_AXES, + RIGHT_HAND, + + LEFT_HAND_CLICK, + RIGHT_HAND_CLICK, + + ACTION1, + ACTION2, + + CONTEXT_MENU, + TOGGLE_MUTE, + + SHIFT, + + // Biseced aliases for TRANSLATE_Z + LONGITUDINAL_BACKWARD, + LONGITUDINAL_FORWARD, + + // Biseced aliases for TRANSLATE_X + LATERAL_LEFT, + LATERAL_RIGHT, + + // Biseced aliases for TRANSLATE_Y + VERTICAL_DOWN, + VERTICAL_UP, + + // Biseced aliases for ROTATE_Y + YAW_LEFT, + YAW_RIGHT, + + // Biseced aliases for ROTATE_X + PITCH_DOWN, + PITCH_UP, + + // Biseced aliases for TRANSLATE_CAMERA_Z + BOOM_IN, + BOOM_OUT, + + NUM_ACTIONS, +}; + + +class ActionsDevice : public QObject, public InputDevice { + Q_OBJECT + Q_PROPERTY(QString name READ getName) + +public: + const QString& getName() const { return _name; } + + // Device functions + virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + ActionsDevice(); + virtual ~ActionsDevice(); +}; + +} + + + + +#include "StandardControls.h" + + +#endif // hifi_StandardController_h diff --git a/libraries/controllers/src/controllers/DeviceProxy.cpp b/libraries/controllers/src/controllers/DeviceProxy.cpp index 9ac701e80d..6cbfc1048d 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.cpp +++ b/libraries/controllers/src/controllers/DeviceProxy.cpp @@ -20,7 +20,7 @@ namespace controller { return getAxis(input, timestamp); case ChannelType::POSE: - return getPose(input, timestamp)._valid ? 1.0f : 0.0f; + return getPose(input, timestamp).valid ? 1.0f : 0.0f; default: return NAN; diff --git a/libraries/controllers/src/controllers/DeviceProxy.h b/libraries/controllers/src/controllers/DeviceProxy.h index 78d13fe5c4..064abdbc7f 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.h +++ b/libraries/controllers/src/controllers/DeviceProxy.h @@ -35,23 +35,15 @@ namespace controller { class DeviceProxy { public: - DeviceProxy(QString name) : _baseName(name), _name(name) {} - const QString& getBaseName() const { return _baseName; } - const QString& getName() const { return _name; } - - ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; - AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; - PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); }; - AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector { return Input::NamedVector(); }; - ResetBindings resetDeviceBindings = [] () -> bool { return true; }; - float getValue(const Input& input, int timestamp = 0) const; - - using Pointer = std::shared_ptr; - - QString _baseName; - QString _name; + using Pointer = std::shared_ptr; + const QString& getName() const { return _name; } + ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; + AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; + PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); }; + AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector const { return Input::NamedVector(); }; + float getValue(const Input& input, int timestamp = 0) const; + QString _name; }; - } #endif diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 923412ac6c..3e4ce94490 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -14,7 +14,9 @@ #include #include -#include "UserInputMapper.h" +#include + +#include "Input.h" class QScriptValue; @@ -24,7 +26,7 @@ namespace controller { * i.e. Hydra.Button0, Standard.X, Action.Yaw */ class Endpoint : public QObject { - Q_OBJECT; + Q_OBJECT; public: using Pointer = std::shared_ptr; using List = std::list; @@ -32,18 +34,18 @@ namespace controller { using ReadLambda = std::function; using WriteLambda = std::function; - Endpoint(const UserInputMapper::Input& input) : _input(input) {} + Endpoint(const Input& input) : _input(input) {} virtual float value() = 0; virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; - const UserInputMapper::Input& getInput() { return _input; } + const Input& getInput() { return _input; } protected: - UserInputMapper::Input _input; + Input _input; }; class LambdaEndpoint : public Endpoint { public: LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {}) - : Endpoint(UserInputMapper::Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } + : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } virtual float value() override { return _readLambda(); } virtual void apply(float newValue, float oldValue, const Pointer& source) override { _writeLambda(newValue); } diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h index 8cc682df70..98377b7434 100644 --- a/libraries/controllers/src/controllers/Input.h +++ b/libraries/controllers/src/controllers/Input.h @@ -27,26 +27,21 @@ enum class ChannelType { // to the Action channels struct Input { union { + 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 _padding : 1; // 2 bits to store the Type directly in the ID + 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 padding : 1; // 2 bits to store the Type directly in the ID }; - uint32_t _id = 0; // by default Input is 0 meaning invalid }; - bool isValid() const { return (_id != 0); } + bool isValid() const { return (id != INVALID_INPUT.id); } - uint16_t getDevice() const { return _device; } - uint16_t getChannel() const { return _channel; } - uint32_t getID() const { return _id; } - ChannelType getType() const { return (ChannelType) _type; } - - void setDevice(uint16_t device) { _device = device; } - void setChannel(uint16_t channel) { _channel = channel; } - void setType(uint16_t type) { _type = type; } - void setID(uint32_t ID) { _id = ID; } + uint16_t getDevice() const { return device; } + uint16_t getChannel() const { return channel; } + uint32_t getID() const { return id; } + ChannelType getType() const { return (ChannelType) type; } bool isButton() const { return getType() == ChannelType::BUTTON; } bool isAxis() const { return getType() == ChannelType::AXIS; } @@ -54,13 +49,13 @@ struct Input { // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2) // where the default initializer (a C++-11ism) for the union data above is not applied. - explicit Input() : _id(0) {} - explicit Input(uint32_t id) : _id(id) {} - explicit Input(uint16_t device, uint16_t channel, ChannelType type) : _device(device), _channel(channel), _type(uint16_t(type)), _padding(0) {} - Input(const Input& src) : _id(src._id) {} - Input& operator = (const Input& src) { _id = src._id; return (*this); } - bool operator ==(const Input& right) const { return _id == right._id; } - bool operator < (const Input& src) const { return _id < src._id; } + explicit Input() {} + explicit Input(uint32_t id) : id(id) {} + explicit Input(uint16_t device, uint16_t channel, ChannelType type) : device(device), channel(channel), type(uint16_t(type)), padding(0) {} + Input(const Input& src) : id(src.id) {} + Input& operator = (const Input& src) { id = src.id; return (*this); } + bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; } + bool operator < (const Input& src) const { return id < src.id; } static const Input INVALID_INPUT; static const uint16_t INVALID_DEVICE; diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index 1f86741b88..78e920d4b5 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -10,75 +10,78 @@ // #include "InputDevice.h" -bool InputDevice::_lowVelocityFilter = false; +#include "Input.h" -const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f; -float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; +namespace controller { -//Constants for getCursorPixelRangeMultiplier() -const float MIN_PIXEL_RANGE_MULT = 0.4f; -const float MAX_PIXEL_RANGE_MULT = 2.0f; -const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; + bool InputDevice::_lowVelocityFilter = false; -//Returns a multiplier to be applied to the cursor range for the controllers -float InputDevice::getCursorPixelRangeMult() { - //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) - return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; -} + const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f; + float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; -float InputDevice::getButton(int channel) const { - if (!_buttonPressedMap.empty()) { - if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { - return 1.0f; + //Constants for getCursorPixelRangeMultiplier() + const float MIN_PIXEL_RANGE_MULT = 0.4f; + const float MAX_PIXEL_RANGE_MULT = 2.0f; + const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; + + //Returns a multiplier to be applied to the cursor range for the controllers + float InputDevice::getCursorPixelRangeMult() { + //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) + return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; + } + + float InputDevice::getButton(int channel) const { + if (!_buttonPressedMap.empty()) { + if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { + return 1.0f; + } else { + return 0.0f; + } } - else { + return 0.0f; + } + + float InputDevice::getAxis(int channel) const { + auto axis = _axisStateMap.find(channel); + if (axis != _axisStateMap.end()) { + return (*axis).second; + } else { return 0.0f; } } - return 0.0f; -} -float InputDevice::getAxis(int channel) const { - auto axis = _axisStateMap.find(channel); - if (axis != _axisStateMap.end()) { - return (*axis).second; + Pose InputDevice::getPose(int channel) const { + auto pose = _poseStateMap.find(channel); + if (pose != _poseStateMap.end()) { + return (*pose).second; + } else { + return Pose(); + } } - else { - return 0.0f; + + Input InputDevice::makeInput(controller::StandardButtonChannel button) { + return Input(_deviceID, button, ChannelType::BUTTON); } -} -UserInputMapper::PoseValue InputDevice::getPose(int channel) const { - auto pose = _poseStateMap.find(channel); - if (pose != _poseStateMap.end()) { - return (*pose).second; + Input InputDevice::makeInput(controller::StandardAxisChannel axis) { + return Input(_deviceID, axis, ChannelType::AXIS); } - else { - return UserInputMapper::PoseValue(); + + Input InputDevice::makeInput(controller::StandardPoseChannel pose) { + return Input(_deviceID, pose, ChannelType::POSE); } -} -UserInputMapper::Input InputDevice::makeInput(controller::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} + Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) { + return Input::NamedPair(makeInput(button), name); + } -UserInputMapper::Input InputDevice::makeInput(controller::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} + Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) { + return Input::NamedPair(makeInput(axis), name); + } -UserInputMapper::Input InputDevice::makeInput(controller::StandardPoseChannel pose) { - return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); -} + Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) { + return Input::NamedPair(makeInput(pose), name); + } -UserInputMapper::InputPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) { - return UserInputMapper::InputPair(makeInput(button), name); -} -UserInputMapper::InputPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) { - return UserInputMapper::InputPair(makeInput(axis), name); } - -UserInputMapper::InputPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) { - return UserInputMapper::InputPair(makeInput(pose), name); -} - diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 4854df0ada..e01def2368 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -10,13 +10,23 @@ // #pragma once -#include "UserInputMapper.h" +#include +#include +#include + +#include + +#include "Pose.h" +#include "Input.h" #include "StandardControls.h" +#include "DeviceProxy.h" // Event types for each controller const unsigned int CONTROLLER_0_EVENT = 1500U; const unsigned int CONTROLLER_1_EVENT = 1501U; +namespace controller { + // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first. // e.g. class Example : public InputPlugin, public InputDevice // instead of class Example : public InputDevice, public InputPlugin @@ -24,17 +34,19 @@ class InputDevice { public: InputDevice(const QString& name) : _name(name) {} + using Pointer = std::shared_ptr; + typedef std::unordered_set ButtonPressedMap; typedef std::map AxisStateMap; - typedef std::map PoseStateMap; + typedef std::map PoseStateMap; // Get current state for each channel float getButton(int channel) const; float getAxis(int channel) const; - UserInputMapper::PoseValue getPose(int channel) const; + Pose getPose(int channel) const; - virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0; + virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) = 0; + virtual QString getDefaultMappingConfig() = 0; // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas @@ -43,6 +55,7 @@ public: virtual void focusOutEvent() = 0; int getDeviceID() { return _deviceID; } + void setDeviceID(int deviceID) { _deviceID = deviceID; } static float getCursorPixelRangeMult(); static float getReticleMoveSpeed() { return _reticleMoveSpeed; } @@ -50,18 +63,18 @@ public: static bool getLowVelocityFilter() { return _lowVelocityFilter; }; - UserInputMapper::Input makeInput(controller::StandardButtonChannel button); - UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); - UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); - UserInputMapper::InputPair makePair(controller::StandardButtonChannel button, const QString& name); - UserInputMapper::InputPair makePair(controller::StandardAxisChannel button, const QString& name); - UserInputMapper::InputPair makePair(controller::StandardPoseChannel button, const QString& name); -public slots: + Input makeInput(StandardButtonChannel button); + Input makeInput(StandardAxisChannel axis); + Input makeInput(StandardPoseChannel pose); + Input::NamedPair makePair(StandardButtonChannel button, const QString& name); + Input::NamedPair makePair(StandardAxisChannel button, const QString& name); + Input::NamedPair makePair(StandardPoseChannel button, const QString& name); + public slots: static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; protected: - - int _deviceID = 0; + friend class UserInputMapper; + uint16_t _deviceID{ Input::INVALID_DEVICE }; QString _name; @@ -73,4 +86,6 @@ protected: private: static float _reticleMoveSpeed; -}; \ No newline at end of file +}; + +} \ No newline at end of file diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/Mapping.cpp index 08e8aef9e8..68c43da393 100644 --- a/libraries/controllers/src/controllers/Mapping.cpp +++ b/libraries/controllers/src/controllers/Mapping.cpp @@ -6,11 +6,3 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "Mapping.h" - -namespace controller { - Mapping::Mapping(const QString& name ) : _name(name) { - - } - -} - diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index e9c8e7a323..ff988bf1b1 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -27,13 +27,11 @@ namespace controller { using Map = std::map; using Pointer = std::shared_ptr; - Mapping(const QString& name); + Mapping(const QString& name) : name(name) {} - Map _channelMappings; + Map channelMappings; - QString _name; - - protected: + QString name; }; } diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index a716955d70..e4674735b0 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -12,17 +12,17 @@ namespace controller { Pose::Pose(const vec3& translation, const quat& rotation, const vec3& velocity, const quat& angularVelocity) : - _translation(translation), _rotation(rotation), _velocity(velocity), _angularVelocity(angularVelocity) { } + translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity) { } bool Pose::operator==(const Pose& right) const { // invalid poses return false for comparison, even against identical invalid poses, like NaN - if (_valid || !right._valid) { + if (!valid || !right.valid) { return false; } // FIXME add margin of error? Or add an additional withinEpsilon function? - return _translation == right.getTranslation() && _rotation == right.getRotation() && - _velocity == right.getVelocity() && _angularVelocity == right.getAngularVelocity(); + return translation == right.getTranslation() && rotation == right.getRotation() && + velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity(); } diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index b77064f2c1..9243ceb734 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -17,11 +17,11 @@ namespace controller { struct Pose { public: - vec3 _translation; - quat _rotation; - vec3 _velocity; - quat _angularVelocity; - bool _valid{ false }; + vec3 translation; + quat rotation; + vec3 velocity; + quat angularVelocity; + bool valid{ false }; Pose() {} Pose(const vec3& translation, const quat& rotation, @@ -30,11 +30,11 @@ namespace controller { Pose(const Pose&) = default; Pose& operator = (const Pose&) = default; bool operator ==(const Pose& right) const; - bool isValid() const { return _valid; } - vec3 getTranslation() const { return _translation; } - quat getRotation() const { return _rotation; } - vec3 getVelocity() const { return _velocity; } - quat getAngularVelocity() const { return _angularVelocity; } + bool isValid() const { return valid; } + vec3 getTranslation() const { return translation; } + quat getRotation() const { return rotation; } + vec3 getVelocity() const { return velocity; } + quat getAngularVelocity() const { return angularVelocity; } }; diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/Route.cpp index 08b6d1f4f2..56590e564a 100644 --- a/libraries/controllers/src/controllers/Route.cpp +++ b/libraries/controllers/src/controllers/Route.cpp @@ -6,5 +6,4 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - #include "Route.h" diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h index 01770a87d1..8b0e70050f 100644 --- a/libraries/controllers/src/controllers/Route.h +++ b/libraries/controllers/src/controllers/Route.h @@ -16,20 +16,17 @@ class QJsonObject; namespace controller { - /* * encapsulates a source, destination and filters to apply */ class Route { public: - Endpoint::Pointer _source; - Endpoint::Pointer _destination; - Filter::List _filters; + Endpoint::Pointer source; + Endpoint::Pointer destination; + Filter::List filters; using Pointer = std::shared_ptr; using List = std::list; - - void parse(const QJsonObject); }; } diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index dc305c4c6e..0f9b5678ca 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -12,8 +12,6 @@ #include -#include -#include #include #include @@ -26,475 +24,146 @@ #include "Logging.h" #include "InputDevice.h" +static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; + +static QVariantMap createDeviceMap(const controller::DeviceProxy::Pointer device) { + auto userInputMapper = DependencyManager::get(); + QVariantMap deviceMap; + for (const auto& inputMapping : device->getAvailabeInputs()) { + const auto& input = inputMapping.first; + const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION); + qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() + << QString::number(input.getID(), 16) << ": " << inputName; + deviceMap.insert(inputName, input.getID()); + } + return deviceMap; +} + +// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block +controller::ScriptingInterface::ScriptingInterface() { + auto userInputMapper = DependencyManager::get(); + + // FIXME make this thread safe + connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, [=] { + updateMaps(); + }); + + + qCDebug(controllers) << "Setting up standard controller abstraction"; + _standard = createDeviceMap(userInputMapper->getStandardDevice()); + + // FIXME allow custom user actions? + auto actionNames = userInputMapper->getActionNames(); + int actionNumber = 0; + qCDebug(controllers) << "Setting up standard actions"; + for (const auto& namedInput : userInputMapper->getActionInputs()) { + const QString& actionName = namedInput.second; + const Input& actionInput = namedInput.first; + qCDebug(controllers) << "\tAction: " << actionName << " " << actionInput.getChannel(); + + // Expose the IDs to JS + QString cleanActionName = QString(actionName).remove(SANITIZE_NAME_EXPRESSION); + _actions.insert(cleanActionName, actionInput.getID()); + } + + updateMaps(); +} + namespace controller { - class VirtualEndpoint : public Endpoint { - public: - VirtualEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT) - : Endpoint(id) { - } - - virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } - - private: - float _currentValue{ 0.0f }; - }; - - - class JSEndpoint : public Endpoint { - public: - JSEndpoint(const QJSValue& callable) - : Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) {} - - virtual float value() { - float result = (float)_callable.call().toNumber();; - return result; - } - - virtual void apply(float newValue, float oldValue, const Pointer& source) { - _callable.call(QJSValueList({ QJSValue(newValue) })); - } - - private: - QJSValue _callable; - }; - - float ScriptEndpoint::value() { - updateValue(); - return _lastValue; - } - - void ScriptEndpoint::updateValue() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); - return; - } - - _lastValue = (float)_callable.call().toNumber(); - } - - void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { - internalApply(newValue, oldValue, source->getInput().getID()); - } - - void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, - Q_ARG(float, newValue), - Q_ARG(float, oldValue), - Q_ARG(int, sourceID)); - return; - } - _callable.call(QScriptValue(), - QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); - } - - class CompositeEndpoint : public Endpoint, Endpoint::Pair { - public: - CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) - : Endpoint(UserInputMapper::Input(UserInputMapper::Input::INVALID_INPUT)), Pair(first, second) { } - - virtual float value() { - float result = first->value() * -1.0f + second->value(); - return result; - } - - virtual void apply(float newValue, float oldValue, const Pointer& source) { - // Composites are read only - } - - private: - Endpoint::Pointer _first; - Endpoint::Pointer _second; - }; - - class ActionEndpoint : public Endpoint { - public: - ActionEndpoint(const UserInputMapper::Input& id = UserInputMapper::Input::INVALID_INPUT) - : Endpoint(id) { - } - - virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { - - _currentValue += newValue; - if (!(_input == UserInputMapper::Input::INVALID_INPUT)) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->deltaActionState(UserInputMapper::Action(_input.getChannel()), newValue); - } - } - - private: - float _currentValue{ 0.0f }; - }; - - QRegularExpression ScriptingInterface::SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; - - QVariantMap createDeviceMap(const UserInputMapper::DeviceProxy* device) { - auto userInputMapper = DependencyManager::get(); - QVariantMap deviceMap; - for (const auto& inputMapping : device->getAvailabeInputs()) { - const auto& input = inputMapping.first; - const auto inputName = QString(inputMapping.second).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); - qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() - << QString::number(input.getID(), 16) << ": " << inputName; - deviceMap.insert(inputName, input.getID()); - } - return deviceMap; - } - - - ScriptingInterface::~ScriptingInterface() { - } - QObject* ScriptingInterface::newMapping(const QString& mappingName) { - if (_mappingsByName.count(mappingName)) { - qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; - } - qDebug() << "Creating new Mapping " << mappingName; - auto mapping = std::make_shared(mappingName); - _mappingsByName[mappingName] = mapping; - return new MappingBuilderProxy(*this, mapping); + auto userInputMapper = DependencyManager::get(); + return new MappingBuilderProxy(*userInputMapper, userInputMapper->newMapping(mappingName)); } - QObject* ScriptingInterface::parseMapping(const QString& json) { - - QJsonObject obj; - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error); - // check validity of the document - if (!doc.isNull()) { - if (doc.isObject()) { - obj = doc.object(); - - auto mapping = std::make_shared("default"); - auto mappingBuilder = new MappingBuilderProxy(*this, mapping); - - mappingBuilder->parse(obj); - - _mappingsByName[mapping->_name] = mapping; - - return mappingBuilder; - } else { - qDebug() << "Mapping json Document is not an object" << endl; - } - } else { - qDebug() << "Invalid JSON...\n"; - qDebug() << error.errorString(); - qDebug() << "JSON was:\n" << json << endl; - - } - - return nullptr; - } - - QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { - QObject* result = nullptr; - auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl)); - if (request) { - QEventLoop eventLoop; - request->setCacheEnabled(false); - connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit); - request->send(); - if (request->getState() != ResourceRequest::Finished) { - eventLoop.exec(); - } - - if (request->getResult() == ResourceRequest::Success) { - result = parseMapping(QString(request->getData())); - } else { - qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl; - } - request->deleteLater(); - } - return result; - } - - Q_INVOKABLE QObject* newMapping(const QJsonObject& json); - void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) { - qCDebug(controllers) << "Attempting to enable mapping " << mappingName; - auto iterator = _mappingsByName.find(mappingName); - if (_mappingsByName.end() == iterator) { - qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; - return; - } - - auto mapping = iterator->second; - if (enable) { - _activeMappings.push_front(mapping); - } else { - auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping); - if (_activeMappings.end() == activeIterator) { - qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName; - return; - } - _activeMappings.erase(activeIterator); - } + auto userInputMapper = DependencyManager::get(); + userInputMapper->enableMapping(mappingName, enable); } float ScriptingInterface::getValue(const int& source) const { - // return (sin(secTimestampNow()) + 1.0f) / 2.0f; - UserInputMapper::Input input(source); - auto iterator = _endpoints.find(input); - if (_endpoints.end() == iterator) { - return 0.0; - } - - const auto& endpoint = iterator->second; - return getValue(endpoint); - } - - float ScriptingInterface::getValue(const Endpoint::Pointer& endpoint) const { - auto valuesIterator = _overrideValues.find(endpoint); - if (_overrideValues.end() != valuesIterator) { - return valuesIterator->second; - } - - return endpoint->value(); + auto userInputMapper = DependencyManager::get(); + return userInputMapper->getValue(Input((uint32_t)source)); } float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const { - return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::BUTTON).getID()); + return getValue(Input(device, source, ChannelType::BUTTON).getID()); } float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const { - return getValue(UserInputMapper::Input(device, source, UserInputMapper::ChannelType::AXIS).getID()); + return getValue(Input(device, source, ChannelType::AXIS).getID()); } Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { return Pose(); } - void ScriptingInterface::update() { - auto userInputMapper = DependencyManager::get(); - static auto deviceNames = userInputMapper->getDeviceNames(); + //bool ScriptingInterface::isPrimaryButtonPressed() const { + // return isButtonPressed(StandardButtonChannel::A); + //} + // + //glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const { + // return getJoystickPosition(0); + //} - if (deviceNames != userInputMapper->getDeviceNames()) { - updateMaps(); - deviceNames = userInputMapper->getDeviceNames(); - } + //int ScriptingInterface::getNumberOfButtons() const { + // return StandardButtonChannel::NUM_STANDARD_BUTTONS; + //} - _overrideValues.clear(); - EndpointSet readEndpoints; - EndpointSet writtenEndpoints; - // Now process the current values for each level of the stack - for (auto& mapping : _activeMappings) { - for (const auto& mappingEntry : mapping->_channelMappings) { - const auto& source = mappingEntry.first; + //bool ScriptingInterface::isButtonPressed(int buttonIndex) const { + // return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true; + //} - // 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 - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when - // I press the button. The exception is if I'm wiring a control back to itself - // in order to adjust my interface, like inverting the Y axis on an analog stick - if (readEndpoints.count(source)) { - continue; - } + //int ScriptingInterface::getNumberOfTriggers() const { + // return StandardCounts::TRIGGERS; + //} - // Apply the value to all the routes - const auto& routes = mappingEntry.second; + //float ScriptingInterface::getTriggerValue(int triggerIndex) const { + // return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT); + //} - for (const auto& route : routes) { - const auto& destination = route->_destination; - // THis could happen if the route destination failed to create - // FIXME: Maybe do not create the route if the destination failed and avoid this case ? - if (!source || !destination) { - continue; - } + //int ScriptingInterface::getNumberOfJoysticks() const { + // return StandardCounts::ANALOG_STICKS; + //} - if (writtenEndpoints.count(destination)) { - continue; - } + //glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const { + // StandardAxisChannel xid = StandardAxisChannel::LX; + // StandardAxisChannel yid = StandardAxisChannel::LY; + // if (joystickIndex != 0) { + // xid = StandardAxisChannel::RX; + // yid = StandardAxisChannel::RY; + // } + // vec2 result; + // result.x = getAxisValue(xid); + // result.y = getAxisValue(yid); + // return result; + //} - // Standard controller destinations can only be can only be used once. - if (userInputMapper->getStandardDeviceID() == destination->getInput().getDevice()) { - writtenEndpoints.insert(destination); - } + //int ScriptingInterface::getNumberOfSpatialControls() const { + // return StandardCounts::POSES; + //} - // Only consume the input if the route isn't a loopback. - // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` - bool loopback = source == destination; - if (!loopback) { - readEndpoints.insert(source); - } + //glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const { + // // FIXME extract the position from the standard pose + // return vec3(); + //} - // Fetch the value, may have been overriden by previous loopback routes - float value = getValue(source); + //glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const { + // // FIXME extract the velocity from the standard pose + // return vec3(); + //} - // Apply each of the filters. - for (const auto& filter : route->_filters) { - value = filter->apply(value); - } + //glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const { + // // FIXME extract the normal from the standard pose + // return vec3(); + //} + // + //glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { + // // FIXME extract the rotation from the standard pose + // return quat(); + //} - if (loopback) { - _overrideValues[source] = value; - } else { - destination->apply(value, 0, source); - } - } - } - } - } - - Endpoint::Pointer ScriptingInterface::endpointFor(const QJSValue& endpoint) { - if (endpoint.isNumber()) { - return endpointFor(UserInputMapper::Input(endpoint.toInt())); - } - - if (endpoint.isCallable()) { - auto result = std::make_shared(endpoint); - return result; - } - - qWarning() << "Unsupported input type " << endpoint.toString(); - return Endpoint::Pointer(); - } - - Endpoint::Pointer ScriptingInterface::endpointFor(const QScriptValue& endpoint) { - if (endpoint.isNumber()) { - return endpointFor(UserInputMapper::Input(endpoint.toInt32())); - } - - if (endpoint.isFunction()) { - auto result = std::make_shared(endpoint); - return result; - } - - qWarning() << "Unsupported input type " << endpoint.toString(); - return Endpoint::Pointer(); - } - - UserInputMapper::Input ScriptingInterface::inputFor(const QString& inputName) { - return DependencyManager::get()->findDeviceInput(inputName); - } - - Endpoint::Pointer ScriptingInterface::endpointFor(const UserInputMapper::Input& inputId) { - auto iterator = _endpoints.find(inputId); - if (_endpoints.end() == iterator) { - qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); - return Endpoint::Pointer(); - } - return iterator->second; - } - - Endpoint::Pointer ScriptingInterface::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { - EndpointPair pair(first, second); - Endpoint::Pointer result; - auto iterator = _compositeEndpoints.find(pair); - if (_compositeEndpoints.end() == iterator) { - result = std::make_shared(first, second); - _compositeEndpoints[pair] = result; - } else { - result = iterator->second; - } - return result; - } - - bool ScriptingInterface::isPrimaryButtonPressed() const { - return isButtonPressed(StandardButtonChannel::A); - } - - glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const { - return getJoystickPosition(0); - } - - int ScriptingInterface::getNumberOfButtons() const { - return StandardButtonChannel::NUM_STANDARD_BUTTONS; - } - - bool ScriptingInterface::isButtonPressed(int buttonIndex) const { - return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0f ? false : true; - } - - int ScriptingInterface::getNumberOfTriggers() const { - return StandardCounts::TRIGGERS; - } - - float ScriptingInterface::getTriggerValue(int triggerIndex) const { - return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT); - } - - int ScriptingInterface::getNumberOfJoysticks() const { - return StandardCounts::ANALOG_STICKS; - } - - glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const { - StandardAxisChannel xid = StandardAxisChannel::LX; - StandardAxisChannel yid = StandardAxisChannel::LY; - if (joystickIndex != 0) { - xid = StandardAxisChannel::RX; - yid = StandardAxisChannel::RY; - } - vec2 result; - result.x = getAxisValue(xid); - result.y = getAxisValue(yid); - return result; - } - - int ScriptingInterface::getNumberOfSpatialControls() const { - return StandardCounts::POSES; - } - - glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const { - // FIXME extract the position from the standard pose - return vec3(); - } - - glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const { - // FIXME extract the velocity from the standard pose - return vec3(); - } - - glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const { - // FIXME extract the normal from the standard pose - return vec3(); - } - - glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { - // FIXME extract the rotation from the standard pose - return quat(); - } - - void ScriptingInterface::updateMaps() { - auto userInputMapper = DependencyManager::get(); - auto devices = userInputMapper->getDevices(); - QSet foundDevices; - for (const auto& deviceMapping : devices) { - auto deviceID = deviceMapping.first; - if (deviceID != userInputMapper->getStandardDeviceID()) { - auto device = deviceMapping.second.get(); - auto deviceName = QString(device->getName()).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); - qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; - foundDevices.insert(device->getName()); - if (_hardware.contains(deviceName)) { - continue; - } - - // Expose the IDs to JS - _hardware.insert(deviceName, createDeviceMap(device)); - - // Create the endpoints - for (const auto& inputMapping : device->getAvailabeInputs()) { - const auto& input = inputMapping.first; - // Ignore aliases - if (_endpoints.count(input)) { - continue; - } - _endpoints[input] = std::make_shared([=] { - auto deviceProxy = userInputMapper->getDeviceProxy(input); - if (!deviceProxy) { - return 0.0f; - } - return deviceProxy->getValue(input, 0); - }); - } - } - } - } - - QVector ScriptingInterface::getAllActions() { + QVector ScriptingInterface::getAllActions() { return DependencyManager::get()->getAllActions(); } @@ -502,7 +171,7 @@ namespace controller { return DependencyManager::get()->getDeviceName((unsigned short)device); } - QVector ScriptingInterface::getAvailableInputs(unsigned int device) { + QVector ScriptingInterface::getAvailableInputs(unsigned int device) { return DependencyManager::get()->getAvailableInputs((unsigned short)device); } @@ -515,7 +184,7 @@ namespace controller { } float ScriptingInterface::getActionValue(int action) { - return DependencyManager::get()->getActionState(UserInputMapper::Action(action)); + return DependencyManager::get()->getActionState(Action(action)); } int ScriptingInterface::findAction(QString actionName) { @@ -526,41 +195,40 @@ namespace controller { return DependencyManager::get()->getActionNames(); } + void ScriptingInterface::updateMaps() { + auto userInputMapper = DependencyManager::get(); + auto devices = userInputMapper->getDevices(); + QSet foundDevices; + for (const auto& deviceMapping : devices) { + auto deviceID = deviceMapping.first; + if (deviceID != userInputMapper->getStandardDeviceID()) { + auto device = deviceMapping.second; + auto deviceName = QString(device->getName()).remove(SANITIZE_NAME_EXPRESSION); + qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; + foundDevices.insert(device->getName()); + if (_hardware.contains(deviceName)) { + continue; + } + + // Expose the IDs to JS + _hardware.insert(deviceName, createDeviceMap(device)); + } + + } + } + + + QObject* ScriptingInterface::parseMapping(const QString& json) { + auto userInputMapper = DependencyManager::get(); + auto mapping = userInputMapper->parseMapping(json); + return new MappingBuilderProxy(*userInputMapper, mapping); + } + + QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { + return nullptr; + } + + } // namespace controllers -using namespace controller; -// FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block -ScriptingInterface::ScriptingInterface() { - auto userInputMapper = DependencyManager::get(); - qCDebug(controllers) << "Setting up standard controller abstraction"; - auto standardDevice = userInputMapper->getStandardDevice(); - // Expose the IDs to JS - _standard = createDeviceMap(standardDevice.get()); - // Create the endpoints - for (const auto& inputMapping : standardDevice->getAvailabeInputs()) { - const auto& standardInput = inputMapping.first; - // Ignore aliases - if (_endpoints.count(standardInput)) { - continue; - } - _endpoints[standardInput] = std::make_shared(standardInput); - } - - // FIXME allow custom user actions? - auto actionNames = userInputMapper->getActionNames(); - int actionNumber = 0; - qCDebug(controllers) << "Setting up standard actions"; - for (const auto& actionName : actionNames) { - UserInputMapper::Input actionInput(UserInputMapper::ACTIONS_DEVICE, actionNumber++, UserInputMapper::ChannelType::AXIS); - qCDebug(controllers) << "\tAction: " << actionName << " " << QString::number(actionInput.getID(), 16); - // Expose the IDs to JS - QString cleanActionName = QString(actionName).remove(ScriptingInterface::SANITIZE_NAME_EXPRESSION); - _actions.insert(cleanActionName, actionInput.getID()); - - // Create the action endpoints - _endpoints[actionInput] = std::make_shared(actionInput); - } - - updateMaps(); -} diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index f473562b9e..b43e065822 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -32,7 +32,6 @@ #include "UserInputMapper.h" #include "StandardControls.h" -#include "Mapping.h" namespace controller { class InputController : public QObject { @@ -65,10 +64,10 @@ namespace controller { public: ScriptingInterface(); - virtual ~ScriptingInterface(); + virtual ~ScriptingInterface() {}; - Q_INVOKABLE QVector getAllActions(); - Q_INVOKABLE QVector getAvailableInputs(unsigned int device); + Q_INVOKABLE QVector getAllActions(); + Q_INVOKABLE QVector getAvailableInputs(unsigned int device); Q_INVOKABLE QString getDeviceName(unsigned int device); Q_INVOKABLE float getActionValue(int action); Q_INVOKABLE int findDevice(QString name); @@ -87,23 +86,23 @@ namespace controller { Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); - Q_INVOKABLE bool isPrimaryButtonPressed() const; - Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; + //Q_INVOKABLE bool isPrimaryButtonPressed() const; + //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; - Q_INVOKABLE int getNumberOfButtons() const; - Q_INVOKABLE bool isButtonPressed(int buttonIndex) const; + //Q_INVOKABLE int getNumberOfButtons() const; + //Q_INVOKABLE bool isButtonPressed(int buttonIndex) const; - Q_INVOKABLE int getNumberOfTriggers() const; - Q_INVOKABLE float getTriggerValue(int triggerIndex) const; + //Q_INVOKABLE int getNumberOfTriggers() const; + //Q_INVOKABLE float getTriggerValue(int triggerIndex) const; - Q_INVOKABLE int getNumberOfJoysticks() const; - Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const; + //Q_INVOKABLE int getNumberOfJoysticks() const; + //Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const; - Q_INVOKABLE int getNumberOfSpatialControls() const; - Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const; - Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const; - Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const; - Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const; + //Q_INVOKABLE int getNumberOfSpatialControls() const; + //Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const; + //Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const; + //Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const; + //Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const; Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; } Q_INVOKABLE const QVariantMap& getActions() { return _actions; } @@ -114,11 +113,7 @@ namespace controller { bool isWheelCaptured() const { return _wheelCaptured; } bool areActionsCaptured() const { return _actionsCaptured; } - static QRegularExpression SANITIZE_NAME_EXPRESSION; - public slots: - virtual void update(); - virtual void updateMaps(); virtual void captureMouseEvents() { _mouseCaptured = true; } virtual void releaseMouseEvents() { _mouseCaptured = false; } @@ -134,61 +129,19 @@ namespace controller { private: - friend class MappingBuilderProxy; - friend class RouteBuilderProxy; - - // FIXME move to unordered set / map - using MappingMap = std::map; - using MappingStack = std::list; - using InputToEndpointMap = std::map; - using EndpointSet = std::unordered_set; - using ValueMap = std::map; - using EndpointPair = std::pair; - using EndpointPairMap = std::map; - - void update(Mapping::Pointer& mapping, EndpointSet& consumed); - float getValue(const Endpoint::Pointer& endpoint) const; - Endpoint::Pointer endpointFor(const QJSValue& endpoint); - Endpoint::Pointer endpointFor(const QScriptValue& endpoint); - Endpoint::Pointer endpointFor(const UserInputMapper::Input& endpoint); - Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); - - UserInputMapper::Input inputFor(const QString& inputName); + // Update the exposed variant maps reporting active hardware + void updateMaps(); QVariantMap _hardware; QVariantMap _actions; QVariantMap _standard; - InputToEndpointMap _endpoints; - EndpointPairMap _compositeEndpoints; - - ValueMap _overrideValues; - MappingMap _mappingsByName; - MappingStack _activeMappings; - bool _mouseCaptured{ false }; bool _touchCaptured{ false }; bool _wheelCaptured{ false }; bool _actionsCaptured{ false }; }; - class ScriptEndpoint : public Endpoint { - Q_OBJECT; - public: - ScriptEndpoint(const QScriptValue& callable) - : Endpoint(UserInputMapper::Input::INVALID_INPUT), _callable(callable) { - } - - virtual float value(); - virtual void apply(float newValue, float oldValue, const Pointer& source); - - protected: - Q_INVOKABLE void updateValue(); - Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); - private: - QScriptValue _callable; - float _lastValue = 0.0f; - }; } diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 5c13c66a07..f5389d1518 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -9,12 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include - #include "StandardController.h" +#include + +#include "DeviceProxy.h" +#include "UserInputMapper.h" + +namespace controller { + +const float CONTROLLER_THRESHOLD = 0.3f; + +StandardController::StandardController() : InputDevice("Standard") { + _deviceID = UserInputMapper::STANDARD_DEVICE; +} + StandardController::~StandardController() { } @@ -26,143 +35,111 @@ void StandardController::focusOutEvent() { _buttonPressedMap.clear(); }; -void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getStandardDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; +void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) { + proxy->_name = _name; + proxy->getButton = [this] (const Input& input, int timestamp) -> bool { return getButton(input.getChannel()); }; + proxy->getAxis = [this] (const Input& input, int timestamp) -> float { return getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; // Buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); + availableInputs.append(Input::NamedPair(makeInput(controller::A), "A")); + availableInputs.append(Input::NamedPair(makeInput(controller::B), "B")); + availableInputs.append(Input::NamedPair(makeInput(controller::X), "X")); + availableInputs.append(Input::NamedPair(makeInput(controller::Y), "Y")); // DPad - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); + availableInputs.append(Input::NamedPair(makeInput(controller::DU), "DU")); + availableInputs.append(Input::NamedPair(makeInput(controller::DD), "DD")); + availableInputs.append(Input::NamedPair(makeInput(controller::DL), "DL")); + availableInputs.append(Input::NamedPair(makeInput(controller::DR), "DR")); // Bumpers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); + availableInputs.append(Input::NamedPair(makeInput(controller::LB), "LB")); + availableInputs.append(Input::NamedPair(makeInput(controller::RB), "RB")); // Stick press - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); + availableInputs.append(Input::NamedPair(makeInput(controller::LS), "LS")); + availableInputs.append(Input::NamedPair(makeInput(controller::RS), "RS")); // Center buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); + availableInputs.append(Input::NamedPair(makeInput(controller::START), "Start")); + availableInputs.append(Input::NamedPair(makeInput(controller::BACK), "Back")); // Analog sticks - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); + availableInputs.append(Input::NamedPair(makeInput(controller::LY), "LY")); + availableInputs.append(Input::NamedPair(makeInput(controller::LX), "LX")); + availableInputs.append(Input::NamedPair(makeInput(controller::RY), "RY")); + availableInputs.append(Input::NamedPair(makeInput(controller::RX), "RX")); // Triggers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); + availableInputs.append(Input::NamedPair(makeInput(controller::LT), "LT")); + availableInputs.append(Input::NamedPair(makeInput(controller::RT), "RT")); // Poses - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT), "LeftPose")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT), "RightPose")); + availableInputs.append(Input::NamedPair(makeInput(controller::LEFT), "LeftPose")); + availableInputs.append(Input::NamedPair(makeInput(controller::RIGHT), "RightPose")); // Aliases, PlayStation style names - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); - - + availableInputs.append(Input::NamedPair(makeInput(controller::LB), "L1")); + availableInputs.append(Input::NamedPair(makeInput(controller::RB), "R1")); + availableInputs.append(Input::NamedPair(makeInput(controller::LT), "L2")); + availableInputs.append(Input::NamedPair(makeInput(controller::RT), "R2")); + availableInputs.append(Input::NamedPair(makeInput(controller::LS), "L3")); + availableInputs.append(Input::NamedPair(makeInput(controller::RS), "R3")); + availableInputs.append(Input::NamedPair(makeInput(controller::BACK), "Select")); + availableInputs.append(Input::NamedPair(makeInput(controller::A), "Cross")); + availableInputs.append(Input::NamedPair(makeInput(controller::B), "Circle")); + availableInputs.append(Input::NamedPair(makeInput(controller::X), "Square")); + availableInputs.append(Input::NamedPair(makeInput(controller::Y), "Triangle")); + availableInputs.append(Input::NamedPair(makeInput(controller::DU), "Up")); + availableInputs.append(Input::NamedPair(makeInput(controller::DD), "Down")); + availableInputs.append(Input::NamedPair(makeInput(controller::DL), "Left")); + availableInputs.append(Input::NamedPair(makeInput(controller::DR), "Right")); return availableInputs; }; - - mapper.registerStandardDevice(proxy); } -void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - // Y axes are flipped (up is negative) - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED); - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LB), BOOM_SPEED); - - // Hold front right shoulder button for precision controls - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); +QString StandardController::getDefaultMappingConfig() { + static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json"; + return DEFAULT_MAPPING_JSON; } -UserInputMapper::Input StandardController::makeInput(controller::StandardButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); -} +// FIXME figure out how to move the shifted version to JSON +//void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { +// const float JOYSTICK_MOVE_SPEED = 1.0f; +// const float DPAD_MOVE_SPEED = 0.5f; +// const float JOYSTICK_YAW_SPEED = 0.5f; +// const float JOYSTICK_PITCH_SPEED = 0.25f; +// const float BOOM_SPEED = 0.1f; +// +// // Hold front right shoulder button for precision controls +// // Left Joystick: Movement, strafing +// mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); +// +// // Right Joystick: Camera orientation +// mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); +// +// // Dpad movement +// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); +// +// // Button controls +// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); +// +// // Zoom +// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); +// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); +// +// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); +// +// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); +// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); +//} -UserInputMapper::Input StandardController::makeInput(controller::StandardAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); -} - -UserInputMapper::Input StandardController::makeInput(controller::StandardPoseChannel pose) { - return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); } diff --git a/libraries/controllers/src/controllers/StandardController.h b/libraries/controllers/src/controllers/StandardController.h index c393af80f4..4aad513553 100644 --- a/libraries/controllers/src/controllers/StandardController.h +++ b/libraries/controllers/src/controllers/StandardController.h @@ -12,37 +12,31 @@ #ifndef hifi_StandardController_h #define hifi_StandardController_h -#include -#include +#include +#include #include "InputDevice.h" - #include "StandardControls.h" -typedef std::shared_ptr StandardControllerPointer; +namespace controller { class StandardController : public QObject, public InputDevice { Q_OBJECT Q_PROPERTY(QString name READ getName) public: - const QString& getName() const { return _name; } // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; - - StandardController() : InputDevice("Standard") {} - ~StandardController(); - - UserInputMapper::Input makeInput(controller::StandardButtonChannel button); - UserInputMapper::Input makeInput(controller::StandardAxisChannel axis); - UserInputMapper::Input makeInput(controller::StandardPoseChannel pose); -private: + StandardController(); + virtual ~StandardController(); }; +} + #endif // hifi_StandardController_h diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 26e03b7719..7b009273a1 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -7,20 +7,156 @@ // #include "UserInputMapper.h" -#include "StandardController.h" +#include +#include + +#include +#include +#include + +#include + +#include "StandardController.h" #include "Logging.h" -const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - (uint16)1; -const uint16_t UserInputMapper::STANDARD_DEVICE = 0; +namespace controller { + const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; + const uint16_t UserInputMapper::STANDARD_DEVICE = 0; +} // Default contruct allocate the poutput size with the current hardcoded action channels -UserInputMapper::UserInputMapper() { - registerStandardDevice(); +controller::UserInputMapper::UserInputMapper() { + _activeMappings.push_back(_defaultMapping); + _standardController = std::make_shared(); + registerDevice(new ActionsDevice()); + registerDevice(_standardController.get()); assignDefaulActionScales(); - createActionNames(); } +namespace controller { + +class ScriptEndpoint : public Endpoint { + Q_OBJECT; +public: + ScriptEndpoint(const QScriptValue& callable) + : Endpoint(Input::INVALID_INPUT), _callable(callable) { + } + + virtual float value(); + virtual void apply(float newValue, float oldValue, const Pointer& source); + +protected: + Q_INVOKABLE void updateValue(); + Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); +private: + QScriptValue _callable; + float _lastValue = 0.0f; +}; + +class VirtualEndpoint : public Endpoint { +public: + VirtualEndpoint(const Input& id = Input::INVALID_INPUT) + : Endpoint(id) { + } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } + +private: + float _currentValue{ 0.0f }; +}; + + +class JSEndpoint : public Endpoint { +public: + JSEndpoint(const QJSValue& callable) + : Endpoint(Input::INVALID_INPUT), _callable(callable) { + } + + virtual float value() { + float result = (float)_callable.call().toNumber();; + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + _callable.call(QJSValueList({ QJSValue(newValue) })); + } + +private: + QJSValue _callable; +}; + +float ScriptEndpoint::value() { + updateValue(); + return _lastValue; +} + +void ScriptEndpoint::updateValue() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + return; + } + + _lastValue = (float)_callable.call().toNumber(); +} + +void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + internalApply(newValue, oldValue, source->getInput().getID()); +} + +void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(float, newValue), + Q_ARG(float, oldValue), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); +} + +class CompositeEndpoint : public Endpoint, Endpoint::Pair { +public: + CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) + : Endpoint(Input(Input::INVALID_INPUT)), Pair(first, second) { } + + virtual float value() { + float result = first->value() * -1.0f + second->value(); + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + // Composites are read only + } + +private: + Endpoint::Pointer _first; + Endpoint::Pointer _second; +}; + +class ActionEndpoint : public Endpoint { +public: + ActionEndpoint(const Input& id = Input::INVALID_INPUT) + : Endpoint(id) { + } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { + + _currentValue += newValue; + if (!(_input == Input::INVALID_INPUT)) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); + } + } + +private: + float _currentValue{ 0.0f }; +}; + + UserInputMapper::~UserInputMapper() { } @@ -32,28 +168,58 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) { return _deviceCounts[deviceName]; } -bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy) { +void UserInputMapper::registerDevice(InputDevice* device) { + if (device->_deviceID == Input::INVALID_DEVICE) { + device->_deviceID = getFreeDeviceID(); + } + const auto& deviceID = device->_deviceID; + DeviceProxy::Pointer proxy = std::make_shared(); + proxy->_name = device->_name; + device->buildDeviceProxy(proxy); + int numberOfType = recordDeviceOfType(proxy->_name); - if (numberOfType > 1) { proxy->_name += QString::number(numberOfType); } - + qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; + + for (const auto& inputMapping : proxy->getAvailabeInputs()) { + const auto& input = inputMapping.first; + // Ignore aliases + if (_endpointsByInput.count(input)) { + continue; + } + Endpoint::Pointer endpoint; + if (input.device == STANDARD_DEVICE) { + endpoint = std::make_shared(input); + } else if (input.device == ACTIONS_DEVICE) { + endpoint = std::make_shared(input); + } else { + endpoint = std::make_shared([=] { + return proxy->getValue(input, 0); + }); + } + _inputsByEndpoint[endpoint] = input; + _endpointsByInput[input] = endpoint; + } + _registeredDevices[deviceID] = proxy; - return true; - + auto mapping = loadMapping(device->getDefaultMappingConfig()); + if (mapping) { + _mappingsByDevice[deviceID] = mapping; + for (const auto& entry : mapping->channelMappings) { + const auto& source = entry.first; + const auto& routes = entry.second; + auto& list = _defaultMapping->channelMappings[source]; + list.insert(list.end(), routes.begin(), routes.end()); + } + } + + emit hardwareChanged(); } - -bool UserInputMapper::registerStandardDevice(const DeviceProxy::Pointer& device) { - device->_name = "Standard"; // Just to make sure - _registeredDevices[getStandardDeviceID()] = device; - return true; -} - - -UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { +DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { auto device = _registeredDevices.find(input.getDevice()); if (device != _registeredDevices.end()) { return (device->second); @@ -69,25 +235,9 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) { return QString("unknown"); } - -void UserInputMapper::resetAllDeviceBindings() { - for (auto device : _registeredDevices) { - device.second->resetDeviceBindings(); - } -} - -void UserInputMapper::resetDevice(uint16 deviceID) { - auto device = _registeredDevices.find(deviceID); - if (device != _registeredDevices.end()) { - device->second->resetDeviceBindings(); - } -} - int UserInputMapper::findDevice(QString name) const { for (auto device : _registeredDevices) { - if (device.second->_name.split(" (")[0] == name) { - return device.first; - } else if (device.second->_baseName == name) { + if (device.second->_name == name) { return device.first; } } @@ -103,8 +253,11 @@ QVector UserInputMapper::getDeviceNames() { return result; } -UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName) const { - +int UserInputMapper::findAction(const QString& actionName) const { + return findDeviceInput("Actions." + actionName).getChannel(); +} + +Input UserInputMapper::findDeviceInput(const QString& inputName) const { // Split the full input name as such: deviceName.inputName auto names = inputName.split('.'); @@ -126,20 +279,9 @@ UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; - } else if (deviceName == "Actions") { - deviceID = ACTIONS_DEVICE; - int actionNum = 0; - for (auto action : _actionNames) { - if (action == inputName) { - return Input(ACTIONS_DEVICE, actionNum, ChannelType::AXIS); - } - actionNum++; - } - - qCDebug(controllers) << "Couldn\'t find ActionChannel named <" << inputName << "> among actions"; - } else { qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">"; + findDevice(deviceName); } } else { qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; @@ -148,100 +290,11 @@ UserInputMapper::Input UserInputMapper::findDeviceInput(const QString& inputName return Input::INVALID_INPUT; } - - -bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { - return addInputChannel(action, input, Input(), scale); -} - -bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { - // Check that the device is registered - if (!getDeviceProxy(input)) { - qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; - return false; - } - - auto inputChannel = InputChannel(input, modifier, action, scale); - - // Insert or replace the input to modifiers - if (inputChannel.hasModifier()) { - auto& modifiers = _inputToModifiersMap[input.getID()]; - modifiers.push_back(inputChannel._modifier); - std::sort(modifiers.begin(), modifiers.end()); - } - - // Now update the action To Inputs side of things - _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); - - return true; -} - -int UserInputMapper::addInputChannels(const InputChannels& channels) { - int nbAdded = 0; - for (auto& channel : channels) { - nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); - } - return nbAdded; -} - -bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { - // Remove from Input to Modifiers map - if (inputChannel.hasModifier()) { - _inputToModifiersMap.erase(inputChannel._input.getID()); - } - - // Remove from Action to Inputs map - std::pair ret; - ret = _actionToInputsMap.equal_range(inputChannel._action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - if (it->second == inputChannel) { - _actionToInputsMap.erase(it); - return true; - } - } - - return false; -} - -void UserInputMapper::removeAllInputChannels() { - _inputToModifiersMap.clear(); - _actionToInputsMap.clear(); -} - -void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { - QVector channels = getAllInputsForDevice(device); - for (auto& channel : channels) { - removeInputChannel(channel); - } -} - +// FIXME remove the associated device mappings void UserInputMapper::removeDevice(int device) { - removeAllInputChannelsForDevice((uint16) device); _registeredDevices.erase(device); } -int UserInputMapper::getInputChannels(InputChannels& channels) const { - for (auto& channel : _actionToInputsMap) { - channels.push_back(channel.second); - } - - return _actionToInputsMap.size(); -} - -QVector UserInputMapper::getAllInputsForDevice(uint16 device) { - InputChannels allChannels; - getInputChannels(allChannels); - - QVector channels; - for (InputChannel inputChannel : allChannels) { - if (inputChannel._input._device == device) { - channels.push_back(inputChannel); - } - } - - return channels; -} - void fixBisectedAxis(float& full, float& negative, float& positive) { full = full + (negative * -1.0f) + positive; negative = full >= 0.0f ? 0.0f : full * -1.0f; @@ -249,64 +302,17 @@ void fixBisectedAxis(float& full, float& negative, float& positive) { } void UserInputMapper::update(float deltaTime) { - // Reset the axis state for next loop for (auto& channel : _actionStates) { channel = 0.0f; } for (auto& channel : _poseStates) { - channel = PoseValue(); + channel = Pose(); } - int currentTimestamp = 0; - for (auto& channelInput : _actionToInputsMap) { - auto& inputMapping = channelInput.second; - auto& inputID = inputMapping._input; - bool enabled = true; - - // Check if this input channel has modifiers and collect the possibilities - auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); - if (modifiersIt != _inputToModifiersMap.end()) { - Modifiers validModifiers; - bool isActiveModifier = false; - for (auto& modifier : modifiersIt->second) { - auto deviceProxy = getDeviceProxy(modifier); - if (deviceProxy->getButton(modifier, currentTimestamp)) { - validModifiers.push_back(modifier); - isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); - } - } - enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; - } - - // if enabled: default input or all modifiers on - if (enabled) { - auto deviceProxy = getDeviceProxy(inputID); - switch (inputMapping._input.getType()) { - case ChannelType::BUTTON: { - _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime - break; - } - case ChannelType::AXIS: { - _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); - break; - } - case ChannelType::POSE: { - if (!_poseStates[channelInput.first].isValid()) { - _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); - } - break; - } - default: { - break; //silence please - } - } - } else{ - // Channel input not enabled - enabled = false; - } - } + // Run the mappings code + update(); // Scale all the channel step with the scale for (auto i = 0; i < NUM_ACTIONS; i++) { @@ -337,7 +343,12 @@ void UserInputMapper::update(float deltaTime) { } } -QVector UserInputMapper::getAllActions() const { +Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { + auto iterator = _registeredDevices.find(deviceID); + return iterator->second->getAvailabeInputs(); +} + +QVector UserInputMapper::getAllActions() const { QVector actions; for (auto i = 0; i < NUM_ACTIONS; i++) { actions.append(Action(i)); @@ -345,31 +356,20 @@ QVector UserInputMapper::getAllActions() const { return actions; } -QVector UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { - QVector inputChannels; - std::pair ret; - ret = _actionToInputsMap.equal_range(action); - for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { - inputChannels.append(it->second); - } - return inputChannels; -} - -int UserInputMapper::findAction(const QString& actionName) const { - auto actions = getAllActions(); - for (auto action : actions) { - if (getActionName(action) == actionName) { - return action; +QString UserInputMapper::getActionName(Action action) const { + for (auto actionPair : getActionInputs()) { + if (actionPair.first.channel == action) { + return actionPair.second; } } - // If the action isn't found, return -1 - return -1; + return QString(); } + QVector UserInputMapper::getActionNames() const { QVector result; - for (auto i = 0; i < NUM_ACTIONS; i++) { - result << _actionNames[i]; + for (auto actionPair : getActionInputs()) { + result << actionPair.second; } return result; } @@ -402,56 +402,18 @@ void UserInputMapper::assignDefaulActionScales() { _actionScales[YAW] = 1.0f; // default } -// This is only necessary as long as the actions are hardcoded -// Eventually you can just add the string when you add the action -void UserInputMapper::createActionNames() { - _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; - _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; - _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; - _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; - _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; - _actionNames[VERTICAL_UP] = "VERTICAL_UP"; - _actionNames[YAW_LEFT] = "YAW_LEFT"; - _actionNames[YAW_RIGHT] = "YAW_RIGHT"; - _actionNames[PITCH_DOWN] = "PITCH_DOWN"; - _actionNames[PITCH_UP] = "PITCH_UP"; - _actionNames[BOOM_IN] = "BOOM_IN"; - _actionNames[BOOM_OUT] = "BOOM_OUT"; - _actionNames[LEFT_HAND] = "LEFT_HAND"; - _actionNames[RIGHT_HAND] = "RIGHT_HAND"; - _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; - _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; - _actionNames[SHIFT] = "SHIFT"; - _actionNames[ACTION1] = "ACTION1"; - _actionNames[ACTION2] = "ACTION2"; - _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; - _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; - _actionNames[TRANSLATE_X] = "TranslateX"; - _actionNames[TRANSLATE_Y] = "TranslateY"; - _actionNames[TRANSLATE_Z] = "TranslateZ"; - _actionNames[ROLL] = "Roll"; - _actionNames[PITCH] = "Pitch"; - _actionNames[YAW] = "Yaw"; -} +static int actionMetaTypeId = qRegisterMetaType(); +static int inputMetaTypeId = qRegisterMetaType(); +static int inputPairMetaTypeId = qRegisterMetaType(); -void UserInputMapper::registerStandardDevice() { - _standardController = std::make_shared(); - _standardController->registerToUserInputMapper(*this); - _standardController->assignDefaultInputMapping(*this); -} +QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); +void inputFromScriptValue(const QScriptValue& object, Input& input); +QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action); +void actionFromScriptValue(const QScriptValue& object, Action& action); +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const InputPair& inputPair); +void inputPairFromScriptValue(const QScriptValue& object, InputPair& inputPair); -static int actionMetaTypeId = qRegisterMetaType(); -static int inputMetaTypeId = qRegisterMetaType(); -static int inputPairMetaTypeId = qRegisterMetaType(); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); -void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); -QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); -void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); -void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); - -QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { +QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) { QScriptValue obj = engine->newObject(); obj.setProperty("device", input.getDevice()); obj.setProperty("channel", input.getChannel()); @@ -460,14 +422,11 @@ QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::In return obj; } -void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { - input.setDevice(object.property("device").toUInt16()); - input.setChannel(object.property("channel").toUInt16()); - input.setType(object.property("type").toUInt16()); - input.setID(object.property("id").toInt32()); +void inputFromScriptValue(const QScriptValue& object, Input& input) { + input.id = object.property("id").toInt32(); } -QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { +QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action) { QScriptValue obj = engine->newObject(); auto userInputMapper = DependencyManager::get(); obj.setProperty("action", (int)action); @@ -475,38 +434,361 @@ QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::A return obj; } -void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { - action = UserInputMapper::Action(object.property("action").toVariant().toInt()); +void actionFromScriptValue(const QScriptValue& object, Action& action) { + action = Action(object.property("action").toVariant().toInt()); } -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const InputPair& inputPair) { QScriptValue obj = engine->newObject(); obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); obj.setProperty("inputName", inputPair.second); return obj; } -void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { +void inputPairFromScriptValue(const QScriptValue& object, InputPair& inputPair) { inputFromScriptValue(object.property("input"), inputPair.first); inputPair.second = QString(object.property("inputName").toVariant().toString()); } void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType >(engine); qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); } -UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { +Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { return Input(STANDARD_DEVICE, button, ChannelType::BUTTON); } -UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) { +Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) { return Input(STANDARD_DEVICE, axis, ChannelType::AXIS); } -UserInputMapper::Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { +Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { return Input(STANDARD_DEVICE, pose, ChannelType::POSE); } + + + +void UserInputMapper::update() { + static auto deviceNames = getDeviceNames(); + _overrideValues.clear(); + + EndpointSet readEndpoints; + EndpointSet writtenEndpoints; + + static const int HARDWARE_PASS = 0; + static const int STANDARD_PASS = 1; + + // Now process the current values for each level of the stack + for (auto& mapping : _activeMappings) { + for (int pass = 0; pass < 2; ++pass) { + for (const auto& mappingEntry : mapping->channelMappings) { + const auto& source = mappingEntry.first; + if (_inputsByEndpoint.count(source)) { + auto sourceInput = _inputsByEndpoint[source]; + if ((sourceInput.device == STANDARD_DEVICE) ^ (pass == STANDARD_PASS)) { + continue; + } + } + + // 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 + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // I press the button. The exception is if I'm wiring a control back to itself + // in order to adjust my interface, like inverting the Y axis on an analog stick + if (readEndpoints.count(source)) { + continue; + } + + // Apply the value to all the routes + const auto& routes = mappingEntry.second; + + for (const auto& route : routes) { + const auto& destination = route->destination; + // THis could happen if the route destination failed to create + // FIXME: Maybe do not create the route if the destination failed and avoid this case ? + if (!destination) { + continue; + } + + if (writtenEndpoints.count(destination)) { + continue; + } + + // Standard controller destinations can only be can only be used once. + if (getStandardDeviceID() == destination->getInput().getDevice()) { + writtenEndpoints.insert(destination); + } + + // Only consume the input if the route isn't a loopback. + // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` + bool loopback = source == destination; + if (!loopback) { + readEndpoints.insert(source); + } + + // Fetch the value, may have been overriden by previous loopback routes + float value = getValue(source); + + // Apply each of the filters. + const auto& filters = route->filters; + for (const auto& filter : route->filters) { + value = filter->apply(value); + } + + if (loopback) { + _overrideValues[source] = value; + } else { + destination->apply(value, 0, source); + } + } + } + } + } +} + +Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { + if (endpoint.isNumber()) { + return endpointFor(Input(endpoint.toInt())); + } + + if (endpoint.isCallable()) { + auto result = std::make_shared(endpoint); + return result; + } + + qWarning() << "Unsupported input type " << endpoint.toString(); + return Endpoint::Pointer(); +} + +Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { + if (endpoint.isNumber()) { + return endpointFor(Input(endpoint.toInt32())); + } + + if (endpoint.isFunction()) { + auto result = std::make_shared(endpoint); + return result; + } + + qWarning() << "Unsupported input type " << endpoint.toString(); + return Endpoint::Pointer(); +} + +Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const { + auto iterator = _endpointsByInput.find(inputId); + if (_endpointsByInput.end() == iterator) { + qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); + return Endpoint::Pointer(); + } + return iterator->second; +} + +Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { + EndpointPair pair(first, second); + Endpoint::Pointer result; + auto iterator = _compositeEndpoints.find(pair); + if (_compositeEndpoints.end() == iterator) { + result = std::make_shared(first, second); + _compositeEndpoints[pair] = result; + } else { + result = iterator->second; + } + return result; +} + + +Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { + if (_mappingsByName.count(mappingName)) { + qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; + } + qDebug() << "Creating new Mapping " << mappingName; + auto mapping = std::make_shared(mappingName); + _mappingsByName[mappingName] = mapping; + return mapping; +} + +// FIXME handle asynchronous loading in the UserInputMapper +//QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { +// QObject* result = nullptr; +// auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl)); +// if (request) { +// QEventLoop eventLoop; +// request->setCacheEnabled(false); +// connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit); +// request->send(); +// if (request->getState() != ResourceRequest::Finished) { +// eventLoop.exec(); +// } +// +// if (request->getResult() == ResourceRequest::Success) { +// result = parseMapping(QString(request->getData())); +// } else { +// qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl; +// } +// request->deleteLater(); +// } +// return result; +//} + + +void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { + qCDebug(controllers) << "Attempting to enable mapping " << mappingName; + auto iterator = _mappingsByName.find(mappingName); + if (_mappingsByName.end() == iterator) { + qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; + return; + } + + auto mapping = iterator->second; + if (enable) { + _activeMappings.push_front(mapping); + } else { + auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping); + if (_activeMappings.end() == activeIterator) { + qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName; + return; + } + _activeMappings.erase(activeIterator); + } +} + +float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { + auto valuesIterator = _overrideValues.find(endpoint); + if (_overrideValues.end() != valuesIterator) { + return valuesIterator->second; + } + + return endpoint->value(); +} + +float UserInputMapper::getValue(const Input& input) const { + auto endpoint = endpointFor(input); + if (!endpoint) { + return 0; + } + return endpoint->value(); +} + + +Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { + if (jsonFile.isEmpty()) { + return Mapping::Pointer(); + } + QString json; + { + QFile file(jsonFile); + if (file.open(QFile::ReadOnly | QFile::Text)) { + json = QTextStream(&file).readAll(); + } + file.close(); + } + return parseMapping(json); +} + + +const QString JSON_NAME = QStringLiteral("name"); +const QString JSON_CHANNELS = QStringLiteral("channels"); +const QString JSON_CHANNEL_FROM = QStringLiteral("from"); +const QString JSON_CHANNEL_TO = QStringLiteral("to"); +const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); + +Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { + if (value.isString()) { + auto input = findDeviceInput(value.toString()); + return endpointFor(input); + } else if (value.isObject()) { + // Endpoint is defined as an object, we expect a js function then + return Endpoint::Pointer(); + } + return Endpoint::Pointer(); +} + +Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { + if (!value.isObject()) { + return Route::Pointer(); + } + + const auto& obj = value.toObject(); + Route::Pointer result = std::make_shared(); + result->source = parseEndpoint(obj[JSON_CHANNEL_FROM]); + if (!result->source) { + qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; + return Route::Pointer(); + } + result->destination = parseEndpoint(obj[JSON_CHANNEL_TO]); + if (!result->destination) { + qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO]; + return Route::Pointer(); + } + + const auto& filtersValue = obj[JSON_CHANNEL_FILTERS]; + if (filtersValue.isArray()) { + auto filtersArray = filtersValue.toArray(); + for (auto filterValue : filtersArray) { + if (filterValue.isObject()) { + qWarning() << "Invalid filter " << filterValue; + return Route::Pointer(); + } + Filter::Pointer filter = Filter::parse(filterValue.toObject()); + if (!filter) { + qWarning() << "Invalid filter " << filterValue; + return Route::Pointer(); + } + result->filters.push_back(filter); + } + } + return result; +} + +Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { + if (!json.isObject()) { + return Mapping::Pointer(); + } + + auto obj = json.toObject(); + auto mapping = std::make_shared("default"); + mapping->name = obj[JSON_NAME].toString(); + mapping->channelMappings.clear(); + const auto& jsonChannels = obj[JSON_CHANNELS].toArray(); + Mapping::Map map; + for (const auto& channelIt : jsonChannels) { + Route::Pointer route = parseRoute(channelIt); + if (!route) { + qWarning() << "Couldn't parse route"; + continue; + } + mapping->channelMappings[route->source].push_back(route); + } + return mapping; +} + +Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { + Mapping::Pointer result; + QJsonObject obj; + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error); + // check validity of the document + if (doc.isNull()) { + return Mapping::Pointer(); + } + + if (!doc.isObject()) { + qWarning() << "Mapping json Document is not an object" << endl; + return Mapping::Pointer(); + } + + // FIXME how did we detect this? + // qDebug() << "Invalid JSON...\n"; + // qDebug() << error.errorString(); + // qDebug() << "JSON was:\n" << json << endl; + //} + return parseMapping(doc.object()); +} + +} + +#include "UserInputMapper.moc" diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index d463ed0482..1c1a9506df 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -15,234 +15,155 @@ #include #include #include + +#include +#include + #include #include #include "Pose.h" #include "Input.h" +#include "InputDevice.h" #include "DeviceProxy.h" #include "StandardControls.h" +#include "Mapping.h" +#include "Endpoint.h" +#include "Actions.h" -class StandardController; -typedef std::shared_ptr StandardControllerPointer; +namespace controller { + class UserInputMapper : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + Q_ENUMS(Action) -class UserInputMapper : public QObject, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - Q_ENUMS(Action) -public: - ~UserInputMapper(); - - using DeviceProxy = controller::DeviceProxy; - using PoseValue = controller::Pose; - using Input = controller::Input; - using ChannelType = controller::ChannelType; - - typedef unsigned short uint16; - typedef unsigned int uint32; - - static void registerControllerTypes(QScriptEngine* engine); - - static const uint16 ACTIONS_DEVICE; - static const uint16 STANDARD_DEVICE; - - - // Modifiers are just button inputID - typedef std::vector< Input > Modifiers; - typedef std::function ButtonGetter; - typedef std::function AxisGetter; - typedef std::function PoseGetter; - typedef QPair InputPair; - typedef std::function ()> AvailableInputGetter; - typedef std::function ResetBindings; - - typedef QVector AvailableInput; - - // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. - uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } - - bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); - bool registerStandardDevice(const DeviceProxy::Pointer& device); - DeviceProxy::Pointer getDeviceProxy(const Input& input); - QString getDeviceName(uint16 deviceID); - QVector getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } - void resetAllDeviceBindings(); - void resetDevice(uint16 deviceID); - int findDevice(QString name) const; - QVector getDeviceNames(); - - Input findDeviceInput(const QString& inputName) const; - - - // Actions are the output channels of the Mapper, that's what the InputChannel map to - // For now the Actions are hardcoded, this is bad, but we will fix that in the near future - enum Action { - TRANSLATE_X = 0, - TRANSLATE_Y, - TRANSLATE_Z, - ROTATE_X, PITCH = ROTATE_X, - ROTATE_Y, YAW = ROTATE_Y, - ROTATE_Z, ROLL = ROTATE_Z, - - TRANSLATE_CAMERA_Z, - NUM_COMBINED_AXES, - - LEFT_HAND = NUM_COMBINED_AXES, - RIGHT_HAND, - - LEFT_HAND_CLICK, - RIGHT_HAND_CLICK, - - ACTION1, - ACTION2, - - CONTEXT_MENU, - TOGGLE_MUTE, - - SHIFT, - - // Biseced aliases for TRANSLATE_Z - LONGITUDINAL_BACKWARD, - LONGITUDINAL_FORWARD, - - // Biseced aliases for TRANSLATE_X - LATERAL_LEFT, - LATERAL_RIGHT, - - // Biseced aliases for TRANSLATE_Y - VERTICAL_DOWN, - VERTICAL_UP, - - // Biseced aliases for ROTATE_Y - YAW_LEFT, - YAW_RIGHT, - - // Biseced aliases for ROTATE_X - PITCH_DOWN, - PITCH_UP, - - // Biseced aliases for TRANSLATE_CAMERA_Z - BOOM_IN, - BOOM_OUT, - - NUM_ACTIONS, - }; - - std::vector _actionNames = std::vector(NUM_ACTIONS); - void createActionNames(); - - QVector getAllActions() const; - QString getActionName(Action action) const { return UserInputMapper::_actionNames[(int) action]; } - float getActionState(Action action) const { return _actionStates[action]; } - PoseValue getPoseState(Action action) const { return _poseStates[action]; } - int findAction(const QString& actionName) const; - QVector getActionNames() const; - void assignDefaulActionScales(); - - void setActionState(Action action, float value) { _externalActionStates[action] = value; } - void deltaActionState(Action action, float delta) { _externalActionStates[action] += delta; } - - // Add input channel to the mapper and check that all the used channels are registered. - // Return true if theinput channel is created correctly, false either - bool addInputChannel(Action action, const Input& input, float scale = 1.0f); - bool addInputChannel(Action action, const Input& input, const Input& modifer, float scale = 1.0f); - - UserInputMapper::Input makeStandardInput(controller::StandardButtonChannel button); - UserInputMapper::Input makeStandardInput(controller::StandardAxisChannel axis); - UserInputMapper::Input makeStandardInput(controller::StandardPoseChannel pose); - - // Under the hood, the input channels are organized in map sorted on the _output - // The InputChannel class is just the full values describing the input channel in one object - class InputChannel { public: - Input _input; - Input _modifier = Input(); // make it invalid by default, meaning no modifier - Action _action = LONGITUDINAL_BACKWARD; - float _scale = 0.0f; - - Input getInput() const { return _input; } - Input getModifier() const { return _modifier; } - Action getAction() const { return _action; } - float getScale() const { return _scale; } - - void setInput(Input input) { _input = input; } - void setModifier(Input modifier) { _modifier = modifier; } - void setAction(Action action) { _action = action; } - void setScale(float scale) { _scale = scale; } + 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 ValueMap = std::map; + using EndpointPair = std::pair; + using EndpointPairMap = std::map; + using DevicesMap = std::map; + using uint16 = uint16_t; + using uint32 = uint32_t; - InputChannel() {} - InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) : - _input(input), _modifier(modifier), _action(action), _scale(scale) {} - InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {} - InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); } - bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; } - bool hasModifier() { return _modifier.isValid(); } + static const uint16_t ACTIONS_DEVICE; + static const uint16_t STANDARD_DEVICE; + + UserInputMapper(); + virtual ~UserInputMapper(); + + + static void registerControllerTypes(QScriptEngine* engine); + + + void registerDevice(InputDevice* device); + DeviceProxy::Pointer getDeviceProxy(const Input& input); + QString getDeviceName(uint16 deviceID); + + Input::NamedVector getAvailableInputs(uint16 deviceID) const; + Input::NamedVector getActionInputs() const { return getAvailableInputs(ACTIONS_DEVICE); } + Input::NamedVector getStandardInputs() const { return getAvailableInputs(STANDARD_DEVICE); } + + int findDevice(QString name) const; + QVector getDeviceNames(); + Input findDeviceInput(const QString& inputName) const; + + QVector getAllActions() const; + QString getActionName(Action action) const; + float getActionState(Action action) const { return _actionStates[action]; } + Pose getPoseState(Action action) const { return _poseStates[action]; } + int findAction(const QString& actionName) const; + QVector getActionNames() const; + void assignDefaulActionScales(); + + void setActionState(Action action, float value) { _externalActionStates[action] = value; } + void deltaActionState(Action action, float delta) { _externalActionStates[action] += delta; } + + static Input makeStandardInput(controller::StandardButtonChannel button); + static Input makeStandardInput(controller::StandardAxisChannel axis); + static Input makeStandardInput(controller::StandardPoseChannel pose); + + void removeDevice(int device); + + // Update means go grab all the device input channels and update the output channel values + void update(float deltaTime); + + void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; } + glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; } + + DevicesMap getDevices() { return _registeredDevices; } + 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); + + void enableMapping(const QString& mappingName, bool enable = true); + float getValue(const Input& input) const; + + signals: + void actionEvent(int action, float state); + void hardwareChanged(); + + protected: + virtual void update(); + // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. + uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } + + InputDevice::Pointer _standardController; + DevicesMap _registeredDevices; + uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; + + std::vector _actionStates = std::vector(NUM_ACTIONS, 0.0f); + std::vector _externalActionStates = std::vector(NUM_ACTIONS, 0.0f); + std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); + std::vector _lastActionStates = std::vector(NUM_ACTIONS, 0.0f); + std::vector _poseStates = std::vector(NUM_ACTIONS); + + glm::mat4 _sensorToWorldMat; + + int recordDeviceOfType(const QString& deviceName); + QHash _deviceCounts; + + float getValue(const Endpoint::Pointer& endpoint) const; + friend class RouteBuilderProxy; + friend class MappingBuilderProxy; + 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); + Mapping::Pointer parseMapping(const QJsonValue& json); + Route::Pointer parseRoute(const QJsonValue& value); + Endpoint::Pointer parseEndpoint(const QJsonValue& value); + + InputToEndpointMap _endpointsByInput; + EndpointToInputMap _inputsByEndpoint; + EndpointPairMap _compositeEndpoints; + + ValueMap _overrideValues; + MappingNameMap _mappingsByName; + Mapping::Pointer _defaultMapping{ std::make_shared("Default") }; + MappingDeviceMap _mappingsByDevice; + MappingStack _activeMappings; }; - typedef std::vector< InputChannel > InputChannels; +} - // Add a bunch of input channels, return the true number of channels that successfully were added - int addInputChannels(const InputChannels& channels); - // Remove the first found instance of the input channel from the input mapper, true if found - bool removeInputChannel(InputChannel channel); - void removeAllInputChannels(); - void removeAllInputChannelsForDevice(uint16 device); - void removeDevice(int device); - //Grab all the input channels currently in use, return the number - int getInputChannels(InputChannels& channels) const; - QVector getAllInputsForDevice(uint16 device); - QVector getInputChannelsForAction(UserInputMapper::Action action); - std::multimap getActionToInputsMap() { return _actionToInputsMap; } +Q_DECLARE_METATYPE(controller::UserInputMapper::InputPair) +Q_DECLARE_METATYPE(controller::Pose) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(controller::Input) +Q_DECLARE_METATYPE(controller::Action) +Q_DECLARE_METATYPE(QVector) - // Update means go grab all the device input channels and update the output channel values - void update(float deltaTime); - - void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; } - glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; } - - UserInputMapper(); - - typedef std::map DevicesMap; - DevicesMap getDevices() { return _registeredDevices; } - - uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } - DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } - -signals: - void actionEvent(int action, float state); - - -protected: - void registerStandardDevice(); - StandardControllerPointer _standardController; - - DevicesMap _registeredDevices; - uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; - - typedef std::map InputToMoModifiersMap; - InputToMoModifiersMap _inputToModifiersMap; - - typedef std::multimap ActionToInputsMap; - ActionToInputsMap _actionToInputsMap; - - std::vector _actionStates = std::vector(NUM_ACTIONS, 0.0f); - std::vector _externalActionStates = std::vector(NUM_ACTIONS, 0.0f); - std::vector _actionScales = std::vector(NUM_ACTIONS, 1.0f); - std::vector _lastActionStates = std::vector(NUM_ACTIONS, 0.0f); - std::vector _poseStates = std::vector(NUM_ACTIONS); - - glm::mat4 _sensorToWorldMat; - - int recordDeviceOfType(const QString& deviceName); - QHash _deviceCounts; -}; - -Q_DECLARE_METATYPE(UserInputMapper::InputPair) -Q_DECLARE_METATYPE(UserInputMapper::PoseValue) -Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(UserInputMapper::Input) -Q_DECLARE_METATYPE(UserInputMapper::InputChannel) -Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(UserInputMapper::Action) -Q_DECLARE_METATYPE(QVector) +// Cheating. +using UserInputMapper = controller::UserInputMapper; #endif // hifi_UserInputMapper_h diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index bde10defdc..462a319a90 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -22,7 +22,7 @@ using namespace controller; QObject* MappingBuilderProxy::from(int input) { qCDebug(controllers) << "Creating new Route builder proxy from " << input; - auto sourceEndpoint = _parent.endpointFor(UserInputMapper::Input(input)); + auto sourceEndpoint = _parent.endpointFor(Input(input)); return from(sourceEndpoint); } @@ -41,7 +41,7 @@ QObject* MappingBuilderProxy::from(const QScriptValue& source) { QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { if (source) { auto route = Route::Pointer(new Route()); - route->_source = source; + route->source = source; return new RouteBuilderProxy(_parent, _mapping, route); } else { qCDebug(controllers) << "MappingBuilderProxy::from : source is null so no route created"; @@ -55,48 +55,8 @@ QObject* MappingBuilderProxy::makeAxis(const QJSValue& source1, const QJSValue& return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } -const QString JSON_NAME = QStringLiteral("name"); -const QString JSON_CHANNELS = QStringLiteral("channels"); -const QString JSON_CHANNEL_FROM = QStringLiteral("from"); -const QString JSON_CHANNEL_TO = QStringLiteral("to"); -const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); - - -void MappingBuilderProxy::parse(const QJsonObject& json) { - _mapping->_name = json[JSON_NAME].toString(); - - _mapping->_channelMappings.clear(); - const auto& jsonChannels = json[JSON_CHANNELS].toArray(); - for (const auto& channelIt : jsonChannels) { - parseRoute(channelIt); - } -} - -void MappingBuilderProxy::parseRoute(const QJsonValue& json) { - if (json.isObject()) { - const auto& jsonChannel = json.toObject(); - - auto newRoute = from(jsonChannel[JSON_CHANNEL_FROM]); - if (newRoute) { - auto route = dynamic_cast(newRoute); - route->filters(jsonChannel[JSON_CHANNEL_FILTERS]); - route->to(jsonChannel[JSON_CHANNEL_TO]); - } - } -} - -QObject* MappingBuilderProxy::from(const QJsonValue& json) { - if (json.isString()) { - return from(_parent.endpointFor(_parent.inputFor(json.toString()))); - } else if (json.isObject()) { - // Endpoint is defined as an object, we expect a js function then - return nullptr; - } - return nullptr; -} - QObject* MappingBuilderProxy::enable(bool enable) { - _parent.enableMapping(_mapping->_name, enable); + _parent.enableMapping(_mapping->name, enable); return this; } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 53db901436..07c1730836 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -22,13 +22,14 @@ class QJsonValue; namespace controller { class ScriptingInterface; +class UserInputMapper; // TODO migrate functionality to a MappingBuilder class and make the proxy defer to that // (for easier use in both C++ and JS) class MappingBuilderProxy : public QObject { Q_OBJECT public: - MappingBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping) + MappingBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping) : _parent(parent), _mapping(mapping) { } Q_INVOKABLE QObject* from(int sourceInput); @@ -39,19 +40,11 @@ public: Q_INVOKABLE QObject* enable(bool enable = true); Q_INVOKABLE QObject* disable() { return enable(false); } - - // JSON route creation point - Q_INVOKABLE QObject* from(const QJsonValue& json); - - - void parse(const QJsonObject& json); -// void serialize(QJsonObject& json); - protected: QObject* from(const Endpoint::Pointer& source); friend class RouteBuilderProxy; - ScriptingInterface& _parent; + UserInputMapper& _parent; Mapping::Pointer _mapping; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index ba2cd60c8b..d60032cb43 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -22,7 +22,7 @@ namespace controller { void RouteBuilderProxy::to(int destinationInput) { qCDebug(controllers) << "Completing route " << destinationInput; - auto destinationEndpoint = _parent.endpointFor(UserInputMapper::Input(destinationInput)); + auto destinationEndpoint = _parent.endpointFor(Input(destinationInput)); return to(destinationEndpoint); } @@ -39,9 +39,9 @@ void RouteBuilderProxy::to(const QScriptValue& destination) { } void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { - auto sourceEndpoint = _route->_source; - _route->_destination = destination; - _mapping->_channelMappings[sourceEndpoint].push_back(_route); + auto sourceEndpoint = _route->source; + _route->destination = destination; + _mapping->channelMappings[sourceEndpoint].push_back(_route); deleteLater(); } @@ -104,37 +104,7 @@ void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { } void RouteBuilderProxy::addFilter(Filter::Pointer filter) { - _route->_filters.push_back(filter); -} - - -QObject* RouteBuilderProxy::filters(const QJsonValue& json) { - // We expect an array of objects to define the filters - if (json.isArray()) { - const auto& jsonFilters = json.toArray(); - for (auto jsonFilter : jsonFilters) { - if (jsonFilter.isObject()) { - // The filter is an object, now let s check for type and potential arguments - Filter::Pointer filter = Filter::parse(jsonFilter.toObject()); - if (filter) { - addFilter(filter); - } - } - } - } - - return this; -} - -void RouteBuilderProxy::to(const QJsonValue& json) { - if (json.isString()) { - - return to(_parent.endpointFor(_parent.inputFor(json.toString()))); - } else if (json.isObject()) { - // Endpoint is defined as an object, we expect a js function then - //return to((Endpoint*) nullptr); - } - + _route->filters.push_back(filter); } } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 66b5e85394..1b66a3d996 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -10,10 +10,11 @@ #define hifi_Controllers_Impl_RouteBuilderProxy_h #include + #include "../Filter.h" #include "../Route.h" #include "../Mapping.h" - +#include "../UserInputMapper.h" class QJSValue; class QScriptValue; class QJsonValue; @@ -27,7 +28,7 @@ class ScriptingInterface; class RouteBuilderProxy : public QObject { Q_OBJECT public: - RouteBuilderProxy(ScriptingInterface& parent, Mapping::Pointer mapping, Route::Pointer route) + RouteBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping, Route::Pointer route) : _parent(parent), _mapping(mapping), _route(route) { } Q_INVOKABLE void to(int destination); @@ -44,15 +45,11 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToInteger(); Q_INVOKABLE QObject* constrainToPositiveInteger(); - // JSON route creation point - Q_INVOKABLE QObject* filters(const QJsonValue& json); - Q_INVOKABLE void to(const QJsonValue& json); - - private: +private: void to(const Endpoint::Pointer& destination); void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); - ScriptingInterface& _parent; + UserInputMapper& _parent; Mapping::Pointer _mapping; Route::Pointer _route; }; diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 4f2a525f07..bb90c04c95 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) AUTOSCRIBE_SHADER_LIB(gpu model render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural model model-networking script-engine controllers render render-utils) +link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils) target_bullet() diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index d3e4e7a629..30074b37d3 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -11,16 +11,15 @@ #include "Joystick.h" -#include -#include +#include const float CONTROLLER_THRESHOLD = 0.3f; #ifdef HAVE_SDL2 const float MAX_AXIS = 32768.0f; -Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : - InputDevice(name), +Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) : + InputDevice("GamePad"), _sdlGameController(sdlGameController), _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), _instanceId(instanceId) @@ -72,136 +71,63 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { #endif +void Joystick::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { + using namespace controller; + proxy->_name = _name; + proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this](const Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this]() -> QVector { + QVector availableInputs{ + makePair(A, "A"), + makePair(B, "B"), + makePair(X, "X"), + makePair(Y, "Y"), + // DPad + makePair(DU, "DU"), + makePair(DD, "DD"), + makePair(DL, "DL"), + makePair(DR, "DR"), + // Bumpers + makePair(LB, "LB"), + makePair(RB, "RB"), + // Stick press + makePair(LS, "LS"), + makePair(RS, "RS"), + // Center buttons + makePair(START, "Start"), + makePair(BACK, "Back"), + // Analog sticks + makePair(LX, "LX"), + makePair(LY, "LY"), + makePair(RX, "RX"), + makePair(RY, "RY"), + + // Triggers + makePair(LT, "LT"), + makePair(RT, "RT"), -void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - // Buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "B")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "X")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Y")); - - // DPad - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "DU")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "DD")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "DL")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "DR")); - - // Bumpers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "RB")); - - // Stick press - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "RS")); - - // Center buttons - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::START), "Start")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Back")); - - // Analog sticks - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RX), "RX")); - - // Triggers - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "RT")); - - // Aliases, PlayStation style names - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LB), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RB), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LT), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RT), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LS), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RS), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::BACK), "Select")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::A), "Cross")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::B), "Circle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::X), "Square")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::Y), "Triangle")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DU), "Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DD), "Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DL), "Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::DR), "Right")); - + // Aliases, PlayStation style names + makePair(LB, "L1"), + makePair(RB, "R1"), + makePair(LT, "L2"), + makePair(RT, "R2"), + makePair(LS, "L3"), + makePair(RS, "R3"), + makePair(BACK, "Select"), + makePair(A, "Cross"), + makePair(B, "Circle"), + makePair(X, "Square"), + makePair(Y, "Triangle"), + makePair(DU, "Up"), + makePair(DD, "Down"), + makePair(DL, "Left"), + makePair(DR, "Right"), + }; return availableInputs; }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerDevice(_deviceID, proxy); } - -void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { -#if 0 -#ifdef HAVE_SDL2 - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float DPAD_MOVE_SPEED = 0.5f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BOOM_SPEED = 0.1f; - - // Y axes are flipped (up is negative) - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LX), JOYSTICK_MOVE_SPEED); - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), JOYSTICK_PITCH_SPEED); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), DPAD_MOVE_SPEED); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), DPAD_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), DPAD_MOVE_SPEED); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), BOOM_SPEED); - - // Hold front right shoulder button for precision controls - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); - - // Dpad movement - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - - // Button controls - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); - - // Zoom - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); -#endif -#endif -} \ No newline at end of file +QString Joystick::getDefaultMappingConfig() { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/xbox.json"; + return MAPPING_JSON; +} diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index 38f00f4f15..a9ed18607c 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -23,7 +23,7 @@ #include #include -class Joystick : public QObject, public InputDevice { +class Joystick : public QObject, public controller::InputDevice { Q_OBJECT Q_PROPERTY(QString name READ getName) @@ -36,16 +36,16 @@ public: const QString& getName() const { return _name; } // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; - Joystick() : InputDevice("Joystick") {} + Joystick() : InputDevice("GamePad") {} ~Joystick(); #ifdef HAVE_SDL2 - Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); + Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController); #endif void closeJoystick(); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 4703d3ae6a..f5f8b47a90 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -14,6 +14,8 @@ #include #include +#include +#include const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; @@ -128,163 +130,153 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { _lastTouch = currentPos; } -UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) { - auto shortCode = (UserInputMapper::uint16)(code & KEYBOARD_MASK); +controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) { + auto shortCode = (uint16_t)(code & KEYBOARD_MASK); if (shortCode != code) { shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys } - return UserInputMapper::Input(_deviceID, shortCode, UserInputMapper::ChannelType::BUTTON); + return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) { +controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) { switch (code) { case Qt::LeftButton: - return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_LEFT, UserInputMapper::ChannelType::BUTTON); + return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_RIGHT, UserInputMapper::ChannelType::BUTTON); + return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_MIDDLE, UserInputMapper::ChannelType::BUTTON); + return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: - return UserInputMapper::Input(); + return controller::Input(); }; } -UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) { + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) { - return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) { + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) { - return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) { + return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); } -void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); - - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; +void KeyboardMouseDevice::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { + using namespace controller; + proxy->getButton = [this] (const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this] (const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + QVector availableInputs; for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) { - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) { - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click")); - availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click")); + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "Left Mouse Click")); + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "Middle Mouse Click")); + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "Right Mouse Click")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down")); return availableInputs; }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerDevice(_deviceID, proxy); } -void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { - const float BUTTON_MOVE_SPEED = 1.0f; - const float BUTTON_YAW_SPEED = 0.75f; - const float BUTTON_PITCH_SPEED = 0.5f; - const float MOUSE_YAW_SPEED = 0.5f; - const float MOUSE_PITCH_SPEED = 0.25f; - const float TOUCH_YAW_SPEED = 0.5f; - const float TOUCH_PITCH_SPEED = 0.25f; - const float BUTTON_BOOM_SPEED = 0.1f; - - // AWSD keys mapping - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_W), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED); - - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); - - // Arrow keys mapping - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED); - - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); - - // Mouse move - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); - - -#ifdef Q_OS_MAC - // wheel event modifier on Mac collide with the touchpad scroll event - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); -#else - // Touch pad yaw pitch - mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); - - // Wheel move - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); - -#endif - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space)); - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T)); +QString KeyboardMouseDevice::getDefaultMappingConfig() { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; + return MAPPING_JSON; } +//void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { +// const float BUTTON_MOVE_SPEED = 1.0f; +// const float BUTTON_YAW_SPEED = 0.75f; +// const float BUTTON_PITCH_SPEED = 0.5f; +// const float MOUSE_YAW_SPEED = 0.5f; +// const float MOUSE_PITCH_SPEED = 0.25f; +// const float TOUCH_YAW_SPEED = 0.5f; +// const float TOUCH_PITCH_SPEED = 0.25f; +// const float BUTTON_BOOM_SPEED = 0.1f; +// +// // AWSD keys mapping +// +// mapper.addInputChannel(controller::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); +// mapper.addInputChannel(controller::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); +// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); +// mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); +// +// // Arrow keys mapping +// mapper.addInputChannel(controller::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED); +// mapper.addInputChannel(controller::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED); +// mapper.addInputChannel(controller::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED); +// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED); +// mapper.addInputChannel(controller::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED); +// mapper.addInputChannel(controller::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED); +// +// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); +// mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); +// +// // Mouse move +// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); +// mapper.addInputChannel(controller::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); +// mapper.addInputChannel(controller::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); +// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); +// +// +//#ifdef Q_OS_MAC +// // wheel event modifier on Mac collide with the touchpad scroll event +// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); +// mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); +// mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); +// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); +//#else +// // Touch pad yaw pitch +// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); +// mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); +// mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); +// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); +// +// // Wheel move +// mapper.addInputChannel(controller::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); +// mapper.addInputChannel(controller::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); +// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED); +// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); +// +//#endif +// +// mapper.addInputChannel(controller::SHIFT, makeInput(Qt::Key_Space)); +// mapper.addInputChannel(controller::ACTION1, makeInput(Qt::Key_R)); +// mapper.addInputChannel(controller::ACTION2, makeInput(Qt::Key_T)); +//} + diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 3c935cab26..f89d877dcd 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -24,7 +24,7 @@ class QKeyEvent; class QMouseEvent; class QWheelEvent; -class KeyboardMouseDevice : public InputPlugin, public InputDevice { +class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice { Q_OBJECT public: enum KeyboardChannel { @@ -72,8 +72,8 @@ public: virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; @@ -91,11 +91,11 @@ public: void wheelEvent(QWheelEvent* event); // Let's make it easy for Qt because we assume we love Qt forever - UserInputMapper::Input makeInput(Qt::Key code); - UserInputMapper::Input makeInput(Qt::MouseButton code); - UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis); - UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis); - UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button); + controller::Input makeInput(Qt::Key code); + controller::Input makeInput(Qt::MouseButton code); + controller::Input makeInput(MouseAxisChannel axis); + controller::Input makeInput(TouchAxisChannel axis); + controller::Input makeInput(TouchButtonChannel button); static const QString NAME; diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index 28874efdb0..b052162c77 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "SDL2Manager.h" @@ -47,11 +48,11 @@ void SDL2Manager::init() { if (controller) { SDL_JoystickID id = getInstanceId(controller); if (!_openJoysticks.contains(id)) { - Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + Joystick* joystick = new Joystick(id, controller); _openJoysticks[id] = joystick; - auto userInputMapper = DependencyManager::get(); - joystick->registerToUserInputMapper(*userInputMapper); - joystick->assignDefaultInputMapping(*userInputMapper); + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(joystick); emit joystickAdded(joystick); } } @@ -92,7 +93,7 @@ void SDL2Manager::pluginFocusOutEvent() { void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { #ifdef HAVE_SDL2 if (_isInitialized) { - auto userInputMapper = DependencyManager::get(); + auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { joystick->update(deltaTime, jointsCaptured); } @@ -124,13 +125,11 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { } else if (event.type == SDL_CONTROLLERDEVICEADDED) { SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); - SDL_JoystickID id = getInstanceId(controller); if (!_openJoysticks.contains(id)) { - Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); - _openJoysticks[id] = joystick; - joystick->registerToUserInputMapper(*userInputMapper); - joystick->assignDefaultInputMapping(*userInputMapper); + // Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); + Joystick* joystick = new Joystick(id, controller); + userInputMapper->registerDevice(joystick); emit joystickAdded(joystick); } } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index b304f260b3..eab3c03740 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -18,10 +18,13 @@ #include #include #include +#include +#include +#include +#include -#include "NumericalConstants.h" #include "SixenseManager.h" -#include "UserActivityLogger.h" + #ifdef HAVE_SIXENSE #include "sixense.h" @@ -119,8 +122,8 @@ void SixenseManager::activate() { loadSettings(); sixenseInit(); _activated = true; - auto userInputMapper = DependencyManager::get(); - registerToUserInputMapper(*userInputMapper); + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(this); #endif } @@ -171,7 +174,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); #endif - auto userInputMapper = DependencyManager::get(); + auto userInputMapper = DependencyManager::get(); if (sixenseGetNumActiveControllers() == 0) { _poseStateMap.clear(); @@ -232,7 +235,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { _poseStateMap.clear(); } } else { - _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = UserInputMapper::PoseValue(); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = controller::Pose(); } } @@ -440,8 +443,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, boo // TODO: find a shortcut with fewer rotations. rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; - _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = - UserInputMapper::PoseValue(position, rotation); + _poseStateMap[left ? controller::StandardPoseChannel::LEFT : controller::StandardPoseChannel::RIGHT] = controller::Pose(position, rotation); #endif // HAVE_SIXENSE } @@ -456,85 +458,91 @@ static const auto R2 = controller::A; static const auto R3 = controller::B; static const auto R4 = controller::Y; -void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); - auto proxy = std::make_shared(_name); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; - using namespace controller; - proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs; - availableInputs.append(UserInputMapper::InputPair(makeInput(L0), "L0")); - availableInputs.append(UserInputMapper::InputPair(makeInput(L1), "L1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(L2), "L2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(L3), "L3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(L4), "L4")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LB), "LB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LS), "LS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LX), "LX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LY), "LY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LT), "LT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(R0), "R0")); - availableInputs.append(UserInputMapper::InputPair(makeInput(R1), "R1")); - availableInputs.append(UserInputMapper::InputPair(makeInput(R2), "R2")); - availableInputs.append(UserInputMapper::InputPair(makeInput(R3), "R3")); - availableInputs.append(UserInputMapper::InputPair(makeInput(R4), "R4")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RB), "RB")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RS), "RS")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RX), "RX")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RY), "RY")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RT), "RT")); - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT), "LeftPose")); - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT), "RightPose")); +using namespace controller; + +void SixenseManager::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { + proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this](const Input& input, int timestamp) -> float { + return this->getAxis(input.getChannel()); + }; + proxy->getPose = [this](const Input& input, int timestamp) -> Pose { return this->getPose(input.getChannel()); }; + proxy->getAvailabeInputs = [this]() -> QVector { + QVector availableInputs; + availableInputs.append(Input::NamedPair(makeInput(L0), "L0")); + availableInputs.append(Input::NamedPair(makeInput(L1), "L1")); + availableInputs.append(Input::NamedPair(makeInput(L2), "L2")); + availableInputs.append(Input::NamedPair(makeInput(L3), "L3")); + availableInputs.append(Input::NamedPair(makeInput(L4), "L4")); + availableInputs.append(Input::NamedPair(makeInput(LB), "LB")); + availableInputs.append(Input::NamedPair(makeInput(LS), "LS")); + availableInputs.append(Input::NamedPair(makeInput(LX), "LX")); + availableInputs.append(Input::NamedPair(makeInput(LY), "LY")); + availableInputs.append(Input::NamedPair(makeInput(LT), "LT")); + availableInputs.append(Input::NamedPair(makeInput(R0), "R0")); + availableInputs.append(Input::NamedPair(makeInput(R1), "R1")); + availableInputs.append(Input::NamedPair(makeInput(R2), "R2")); + availableInputs.append(Input::NamedPair(makeInput(R3), "R3")); + availableInputs.append(Input::NamedPair(makeInput(R4), "R4")); + availableInputs.append(Input::NamedPair(makeInput(RB), "RB")); + availableInputs.append(Input::NamedPair(makeInput(RS), "RS")); + availableInputs.append(Input::NamedPair(makeInput(RX), "RX")); + availableInputs.append(Input::NamedPair(makeInput(RY), "RY")); + availableInputs.append(Input::NamedPair(makeInput(RT), "RT")); + availableInputs.append(Input::NamedPair(makeInput(LEFT), "LeftPose")); + availableInputs.append(Input::NamedPair(makeInput(RIGHT), "RightPose")); return availableInputs; }; - mapper.registerDevice(_deviceID, proxy); } -void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - const float JOYSTICK_YAW_SPEED = 0.5f; - const float JOYSTICK_PITCH_SPEED = 0.25f; - const float BUTTON_MOVE_SPEED = 1.0f; - const float BOOM_SPEED = 0.1f; - using namespace controller; - - // Left Joystick: Movement, strafing - mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(LY), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(LX), JOYSTICK_MOVE_SPEED); - - // Right Joystick: Camera orientation - mapper.addInputChannel(UserInputMapper::YAW, makeInput(RX), JOYSTICK_YAW_SPEED); - mapper.addInputChannel(UserInputMapper::PITCH, makeInput(RY), JOYSTICK_PITCH_SPEED); - - // Buttons - mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(L3), BOOM_SPEED); - mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(L1), BOOM_SPEED); - - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(R3), BUTTON_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(R1), BUTTON_MOVE_SPEED); - - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(L2)); - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(R2)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(L4)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(R4)); - - // FIXME -// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); -// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); - - mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(LT)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(RT)); - - // TODO find a mechanism to allow users to navigate the context menu via - mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(L0)); - mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(R0)); +QString SixenseManager::getDefaultMappingConfig() { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json"; + return MAPPING_JSON; } +// +//void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { +// const float JOYSTICK_MOVE_SPEED = 1.0f; +// const float JOYSTICK_YAW_SPEED = 0.5f; +// const float JOYSTICK_PITCH_SPEED = 0.25f; +// const float BUTTON_MOVE_SPEED = 1.0f; +// const float BOOM_SPEED = 0.1f; +// using namespace controller; +// +// // Left Joystick: Movement, strafing +// mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(LY), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(LX), JOYSTICK_MOVE_SPEED); +// +// // Right Joystick: Camera orientation +// mapper.addInputChannel(UserInputMapper::YAW, makeInput(RX), JOYSTICK_YAW_SPEED); +// mapper.addInputChannel(UserInputMapper::PITCH, makeInput(RY), JOYSTICK_PITCH_SPEED); +// +// // Buttons +// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(L3), BOOM_SPEED); +// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(L1), BOOM_SPEED); +// +// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(R3), BUTTON_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(R1), BUTTON_MOVE_SPEED); +// +// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(L2)); +// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(R2)); +// +// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(L4)); +// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(R4)); +// +// // FIXME +//// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); +//// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); +// +// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(LT)); +// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(RT)); +// +// // TODO find a mechanism to allow users to navigate the context menu via +// mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(L0)); +// mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(R0)); +// +//} + // virtual void SixenseManager::saveSettings() const { Settings settings; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 897ca72940..91cdb5f60e 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -42,7 +42,7 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; // Handles interaction with the Sixense SDK (e.g., Razer Hydra). -class SixenseManager : public InputPlugin, public InputDevice { +class SixenseManager : public InputPlugin, public controller::InputDevice { Q_OBJECT public: SixenseManager(); @@ -60,8 +60,8 @@ public: virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index a3374cc1c0..1c8d8c2add 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -12,15 +12,19 @@ #include "ViveControllerManager.h" #include - -#include "GeometryCache.h" +#include +#include #include #include #include #include -#include "NumericalConstants.h" +#include #include -#include "UserActivityLogger.h" +#include + +#include + +#include #ifdef Q_OS_WIN extern vr::IVRSystem* _hmd; @@ -29,18 +33,6 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount] extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; #endif -const unsigned int LEFT_MASK = 0U; -const unsigned int RIGHT_MASK = 1U; - -const uint64_t VR_BUTTON_A = 1U << 1; -const uint64_t VR_GRIP_BUTTON = 1U << 2; -const uint64_t VR_TRACKPAD_BUTTON = 1ULL << 32; -const uint64_t VR_TRIGGER_BUTTON = 1ULL << 33; - -const unsigned int BUTTON_A = 1U << 1; -const unsigned int GRIP_BUTTON = 1U << 2; -const unsigned int TRACKPAD_BUTTON = 1U << 3; -const unsigned int TRIGGER_BUTTON = 1U << 4; const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; @@ -173,8 +165,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint //pendingChanges.updateItem(_leftHandRenderID, ); - UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()]; - UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()]; + controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT]; + controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT]; gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); @@ -186,21 +178,20 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint //batch._glBindTexture(GL_TEXTURE_2D, _uexture); if (leftHand.isValid()) { - renderHand(leftHand, batch, LEFT_HAND); + renderHand(leftHand, batch, 1); } if (rightHand.isValid()) { - renderHand(rightHand, batch, RIGHT_HAND); + renderHand(rightHand, batch, -1); } }); } } -void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index) { - auto userInputMapper = DependencyManager::get(); +void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) { + auto userInputMapper = DependencyManager::get(); Transform transform(userInputMapper->getSensorToWorldMat()); transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET)); - int sign = index == LEFT_HAND ? 1 : -1; glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f)); transform.postRotate(rotation); @@ -251,6 +242,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } numTrackedControllers++; + bool left = numTrackedControllers == 1; const mat4& mat = _trackedDevicePoseMat4[device]; @@ -264,14 +256,18 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: "); //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y; //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y; - handleButtonEvent(controllerState.ulButtonPressed, numTrackedControllers - 1); - for (int i = 0; i < vr::k_unControllerStateAxisCount; i++) { - handleAxisEvent(Axis(i), controllerState.rAxis[i].x, controllerState.rAxis[i].y, numTrackedControllers - 1); + for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { + auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); + bool pressed = 0 != (controllerState.ulButtonPressed & mask); + handleButtonEvent(i, pressed, left); + } + for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { + handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left); } } } - auto userInputMapper = DependencyManager::get(); + auto userInputMapper = DependencyManager::get(); if (numTrackedControllers == 0) { if (_deviceID != 0) { @@ -282,8 +278,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } if (_trackedControllers == 0 && numTrackedControllers > 0) { - registerToUserInputMapper(*userInputMapper); - assignDefaultInputMapping(*userInputMapper); + userInputMapper->registerDevice(this); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); } @@ -296,33 +291,41 @@ void ViveControllerManager::focusOutEvent() { _buttonPressedMap.clear(); }; -void ViveControllerManager::handleAxisEvent(Axis axis, float x, float y, int index) { - if (axis == TRACKPAD_AXIS) { - _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (y > 0.0f) ? y : 0.0f; - _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (y < 0.0f) ? -y : 0.0f; - _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (x > 0.0f) ? x : 0.0f; - _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (x < 0.0f) ? -x : 0.0f; - } else if (axis == TRIGGER_AXIS) { - _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = x; +// These functions do translation from the Steam IDs to the standard controller IDs +void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { +#ifdef Q_OS_WIN + using namespace controller; + if (axis == vr::k_EButton_SteamVR_Touchpad) { + _axisStateMap[left ? LX : RX] = x; + _axisStateMap[left ? LY : RY] = y; + } else if (axis == vr::k_EButton_SteamVR_Trigger) { + _axisStateMap[left ? LT : RT] = x; } +#endif } -void ViveControllerManager::handleButtonEvent(uint64_t buttons, int index) { - if (buttons & VR_BUTTON_A) { - _buttonPressedMap.insert(makeInput(BUTTON_A, index).getChannel()); +// These functions do translation from the Steam IDs to the standard controller IDs +void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) { +#ifdef Q_OS_WIN + if (!pressed) { + return; } - if (buttons & VR_GRIP_BUTTON) { - _buttonPressedMap.insert(makeInput(GRIP_BUTTON, index).getChannel()); - } - if (buttons & VR_TRACKPAD_BUTTON) { - _buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel()); - } - if (buttons & VR_TRIGGER_BUTTON) { - _buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel()); + + if (button == vr::k_EButton_ApplicationMenu) { + // FIXME? + _buttonPressedMap.insert(left ? controller::A : controller::A); + } else if (button == vr::k_EButton_Grip) { + // Tony says these are harder to reach, so make them the meta buttons + _buttonPressedMap.insert(left ? controller::BACK : controller::START); + } else if (button == vr::k_EButton_SteamVR_Trigger) { + _buttonPressedMap.insert(left ? controller::LB : controller::RB); + } else if (button == vr::k_EButton_SteamVR_Touchpad) { + _buttonPressedMap.insert(left ? controller::LS : controller::RS); } +#endif } -void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { +void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::quat_cast(mat); @@ -378,7 +381,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - float sign = (index == LEFT_HAND) ? -1.0f : 1.0f; + float sign = left ? -1.0f : 1.0f; const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f)); const glm::quat offset = glm::inverse(signedQuaterZ * eighthX); @@ -386,92 +389,93 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET); - _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); + _poseStateMap[left ? controller::LEFT : controller::RIGHT] = controller::Pose(position, rotation); } -void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) { - // Grab the current free device ID - _deviceID = mapper.getFreeDeviceID(); - - auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name)); - proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand")); - - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); +void ViveControllerManager::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { + using namespace controller; + proxy->_name = _name; + proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; + proxy->getAxis = [this](const Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; + proxy->getPose = [this](const Input& input, int timestamp) -> Pose { return this->getPose(input.getChannel()); }; + proxy->getAvailabeInputs = [this]() -> QVector { + QVector availableInputs{ + // Trackpad analogs + makePair(LX, "LX"), + makePair(LY, "LY"), + makePair(RX, "RX"), + makePair(RY, "RY"), + // trigger analogs + makePair(LT, "LT"), + makePair(RT, "RT"), - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); + makePair(LB, "LB"), + makePair(RB, "RB"), - - availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand")); + makePair(LS, "LS"), + makePair(RS, "RS"), + }; - availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 1), "Right Button A")); - availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); - availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); + //availableInputs.append(Input::NamedPair(makeInput(LEFT_HAND), "Left Hand")); + + //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); + //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); + + //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up")); + //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down")); + //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right")); + //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left")); + //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); + + + //availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand")); + + //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A")); + //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); + + //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up")); + //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down")); + //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right")); + //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left")); + //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right")); - availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left")); - availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger")); - return availableInputs; }; - proxy->resetDeviceBindings = [this, &mapper] () -> bool { - mapper.removeAllInputChannelsForDevice(_deviceID); - this->assignDefaultInputMapping(mapper); - return true; - }; - mapper.registerDevice(_deviceID, proxy); } -void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { - const float JOYSTICK_MOVE_SPEED = 1.0f; - - // Left Trackpad: Movement, strafing - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); - - // Right Trackpad: Vertical movement, zooming - mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); - mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); - - // Buttons - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0)); - mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1)); - - mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0)); - mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1)); - - mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); - - // Hands - mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); - mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); +QString ViveControllerManager::getDefaultMappingConfig() { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; + return MAPPING_JSON; } -UserInputMapper::Input ViveControllerManager::makeInput(unsigned int button, int index) { - return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); -} - -UserInputMapper::Input ViveControllerManager::makeInput(ViveControllerManager::JoystickAxisChannel axis, int index) { - return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); -} - -UserInputMapper::Input ViveControllerManager::makeInput(JointChannel joint) { - return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE); -} +//void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { +// const float JOYSTICK_MOVE_SPEED = 1.0f; +// +// // Left Trackpad: Movement, strafing +// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); +// +// // Right Trackpad: Vertical movement, zooming +// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); +// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); +// +// // Buttons +// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0)); +// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1)); +// +// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0)); +// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1)); +// +// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); +// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); +// +// // Hands +// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); +// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); +//} diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index bcc27d07ae..67ad75c9e8 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -24,30 +24,9 @@ #include #include -class ViveControllerManager : public InputPlugin, public InputDevice { +class ViveControllerManager : public InputPlugin, public controller::InputDevice { Q_OBJECT public: - enum JoystickAxisChannel { - AXIS_Y_POS = 1U << 1, - AXIS_Y_NEG = 1U << 2, - AXIS_X_POS = 1U << 3, - AXIS_X_NEG = 1U << 4, - BACK_TRIGGER = 1U << 5, - }; - - enum Axis { - TRACKPAD_AXIS = 0, - TRIGGER_AXIS, - AXIS_2, - AXIS_3, - AXIS_4, - }; - - enum JointChannel { - LEFT_HAND = 0, - RIGHT_HAND, - }; - ViveControllerManager(); // Plugin functions @@ -62,8 +41,8 @@ public: virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } // Device functions - virtual void registerToUserInputMapper(UserInputMapper& mapper) override; - virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; + virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; @@ -71,16 +50,12 @@ public: void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } - UserInputMapper::Input makeInput(unsigned int button, int index); - UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); - UserInputMapper::Input makeInput(JointChannel joint); - private: - void renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index); + void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - void handleButtonEvent(uint64_t buttons, int index); - void handleAxisEvent(Axis axis, float x, float y, int index); - void handlePoseEvent(const mat4& mat, int index); + void handleButtonEvent(uint32_t button, bool pressed, bool left); + void handleAxisEvent(uint32_t axis, float x, float y, bool left); + void handlePoseEvent(const mat4& mat, bool left); int _trackedControllers; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index dc08fd7a69..40de50b153 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -28,7 +28,9 @@ #include #include -#include "AnimationObject.h" +#include +#include + #include "ArrayBufferViewClass.h" #include "BatchLoader.h" #include "DataViewClass.h" diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5a0fb64ae2..9cf1bf1e18 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -26,8 +26,6 @@ #include #include #include -#include - #include "MouseEvent.h" #include "ArrayBufferClass.h" diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index eb080700fa..bc0ad179ef 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -123,20 +123,20 @@ int main(int argc, char** argv) { inputPlugin->pluginUpdate(delta, false); } - auto userInputMapper = DependencyManager::get(); + auto userInputMapper = DependencyManager::get(); userInputMapper->update(delta); }); timer.start(50); { - DependencyManager::set(); + DependencyManager::set(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { QString name = inputPlugin->getName(); inputPlugin->activate(); - auto userInputMapper = DependencyManager::get(); + auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky - keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); + userInputMapper->registerDevice(keyboardMouseDevice); } inputPlugin->pluginUpdate(0, false); } From 22139931f252966396538fb75ef2bd091c89470b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 10:35:53 -0700 Subject: [PATCH 071/232] Warning fixes --- .../src/controllers/ScriptingInterface.cpp | 1 - .../src/controllers/StandardController.cpp | 2 -- .../controllers/src/controllers/UserInputMapper.cpp | 12 ++++++------ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 0f9b5678ca..b10c4a83b0 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -54,7 +54,6 @@ controller::ScriptingInterface::ScriptingInterface() { // FIXME allow custom user actions? auto actionNames = userInputMapper->getActionNames(); - int actionNumber = 0; qCDebug(controllers) << "Setting up standard actions"; for (const auto& namedInput : userInputMapper->getActionInputs()) { const QString& actionName = namedInput.second; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index f5389d1518..304f1e87aa 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -18,8 +18,6 @@ namespace controller { -const float CONTROLLER_THRESHOLD = 0.3f; - StandardController::StandardController() : InputDevice("Standard") { _deviceID = UserInputMapper::STANDARD_DEVICE; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 7b009273a1..0c3a43bd34 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -470,7 +470,11 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { return Input(STANDARD_DEVICE, pose, ChannelType::POSE); } - +enum Pass { + HARDWARE_PASS, + STANDARD_PASS, + NUM_PASSES +}; void UserInputMapper::update() { static auto deviceNames = getDeviceNames(); @@ -479,12 +483,9 @@ void UserInputMapper::update() { EndpointSet readEndpoints; EndpointSet writtenEndpoints; - static const int HARDWARE_PASS = 0; - static const int STANDARD_PASS = 1; - // Now process the current values for each level of the stack for (auto& mapping : _activeMappings) { - for (int pass = 0; pass < 2; ++pass) { + for (int pass = HARDWARE_PASS; pass < NUM_PASSES; ++pass) { for (const auto& mappingEntry : mapping->channelMappings) { const auto& source = mappingEntry.first; if (_inputsByEndpoint.count(source)) { @@ -534,7 +535,6 @@ void UserInputMapper::update() { float value = getValue(source); // Apply each of the filters. - const auto& filters = route->filters; for (const auto& filter : route->filters) { value = filter->apply(value); } From eacd6af03232c15e5fc5220d046700eb52427fa3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 14:14:13 -0700 Subject: [PATCH 072/232] Get conditional routes working --- .../resources/controllers/keyboardMouse.json | 11 +- .../src/controllers/Conditional.cpp | 31 +++ .../controllers/src/controllers/Conditional.h | 63 +++++++ .../controllers/src/controllers/Filter.cpp | 24 ++- .../controllers/src/controllers/Filter.h | 85 ++------- .../controllers/src/controllers/Mapping.h | 6 +- libraries/controllers/src/controllers/Route.h | 4 +- .../src/controllers/UserInputMapper.cpp | 178 +++++++++++------- .../src/controllers/UserInputMapper.h | 1 + .../controllers/impl/RouteBuilderProxy.cpp | 2 +- .../src/input-plugins/KeyboardMouseDevice.cpp | 30 +-- libraries/shared/src/shared/Factory.h | 51 +++++ 12 files changed, 310 insertions(+), 176 deletions(-) create mode 100644 libraries/controllers/src/controllers/Conditional.cpp create mode 100644 libraries/controllers/src/controllers/Conditional.h create mode 100644 libraries/shared/src/shared/Factory.h diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 71450a0c48..7b9a8d733a 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -1,22 +1,25 @@ { "name": "Keyboard/Mouse to Actions", "channels": [ - { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + + { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" }, { "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, diff --git a/libraries/controllers/src/controllers/Conditional.cpp b/libraries/controllers/src/controllers/Conditional.cpp new file mode 100644 index 0000000000..7173c80b76 --- /dev/null +++ b/libraries/controllers/src/controllers/Conditional.cpp @@ -0,0 +1,31 @@ +// +// 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 +// + +#include "Conditional.h" + +#include + +#include "Endpoint.h" + +namespace controller { + + Conditional::Pointer Conditional::parse(const QJsonValue& json) { + return Conditional::Pointer(); + } + + bool EndpointConditional::satisfied() { + if (!_endpoint) { + return false; + } + auto value = _endpoint->value(); + if (value == 0.0f) { + return false; + } + return true; + } +} diff --git a/libraries/controllers/src/controllers/Conditional.h b/libraries/controllers/src/controllers/Conditional.h new file mode 100644 index 0000000000..d0226d5775 --- /dev/null +++ b/libraries/controllers/src/controllers/Conditional.h @@ -0,0 +1,63 @@ +// +// 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_Conditional_h +#define hifi_Controllers_Conditional_h + +#include +#include + +#include + +#include + +#include "Endpoint.h" + +class QJsonValue; + +namespace controller { + /* + * encapsulates a source, destination and filters to apply + */ + class Conditional { + public: + using Pointer = std::shared_ptr; + using List = std::list; + using Factory = hifi::SimpleFactory; + + virtual bool satisfied() = 0; + virtual bool parseParameters(const QJsonValue& parameters) { return true; } + + static Pointer parse(const QJsonValue& json); + static void registerBuilder(const QString& name, Factory::Builder builder); + static Factory& getFactory() { return _factory; } + protected: + static Factory _factory; + }; + + class EndpointConditional : public Conditional { + public: + EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) { } + virtual bool satisfied() override; + private: + Endpoint::Pointer _endpoint; + }; + +} + +#define REGISTER_CONDITIONAL_CLASS(classEntry) \ + private: \ + using Registrar = Conditional::Factory::Registrar; \ + static Registrar _registrar; + +#define REGISTER_CONDITIONAL_CLASS_INSTANCE(classEntry, className) \ + classEntry::Registrar classEntry::_registrar(className, Conditional::getFactory()); + + +#endif diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/Filter.cpp index 94fb87c48e..bc31e9ea44 100644 --- a/libraries/controllers/src/controllers/Filter.cpp +++ b/libraries/controllers/src/controllers/Filter.cpp @@ -18,18 +18,27 @@ using namespace controller; +Filter::Factory Filter::_factory; + +REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") +REGISTER_FILTER_CLASS_INSTANCE(ConstrainToIntegerFilter, "constrainToInteger") +REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger") +REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") +REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp") +REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") +REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") + const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); -Filter::Factory Filter::_factory; Filter::Pointer Filter::parse(const QJsonObject& json) { // The filter is an object, now let s check for type and potential arguments Filter::Pointer filter; auto filterType = json[JSON_FILTER_TYPE]; if (filterType.isString()) { - filter.reset(Filter::getFactory().create(filterType.toString().toStdString())); + filter = Filter::getFactory().create(filterType.toString()); if (filter) { // Filter is defined, need to read the parameters and validate auto parameters = json[JSON_FILTER_PARAMS]; @@ -44,7 +53,6 @@ Filter::Pointer Filter::parse(const QJsonObject& json) { return Filter::Pointer(); } -ScaleFilter::FactoryEntryBuilder ScaleFilter::_factoryEntryBuilder; bool ScaleFilter::parseParameters(const QJsonArray& parameters) { if (parameters.size() > 1) { @@ -53,10 +61,6 @@ bool ScaleFilter::parseParameters(const QJsonArray& parameters) { return true; } -InvertFilter::FactoryEntryBuilder InvertFilter::_factoryEntryBuilder; - - -ClampFilter::FactoryEntryBuilder ClampFilter::_factoryEntryBuilder; bool ClampFilter::parseParameters(const QJsonArray& parameters) { if (parameters.size() > 1) { @@ -68,7 +72,6 @@ bool ClampFilter::parseParameters(const QJsonArray& parameters) { return true; } -DeadZoneFilter::FactoryEntryBuilder DeadZoneFilter::_factoryEntryBuilder; float DeadZoneFilter::apply(float value) const { float scale = 1.0f / (1.0f - _min); @@ -85,8 +88,6 @@ bool DeadZoneFilter::parseParameters(const QJsonArray& parameters) { return true; } -PulseFilter::FactoryEntryBuilder PulseFilter::_factoryEntryBuilder; - float PulseFilter::apply(float value) const { float result = 0.0f; @@ -110,7 +111,4 @@ bool PulseFilter::parseParameters(const QJsonArray& parameters) { return true; } -ConstrainToIntegerFilter::FactoryEntryBuilder ConstrainToIntegerFilter::_factoryEntryBuilder; - -ConstrainToPositiveIntegerFilter::FactoryEntryBuilder ConstrainToPositiveIntegerFilter::_factoryEntryBuilder; diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/Filter.h index 876f57c97d..1fa9833044 100644 --- a/libraries/controllers/src/controllers/Filter.h +++ b/libraries/controllers/src/controllers/Filter.h @@ -13,9 +13,10 @@ #include #include #include -#include #include +#include + #include #include @@ -23,81 +24,35 @@ class QJsonObject; class QJsonArray; - namespace controller { // Encapsulates part of a filter chain class Filter { public: - virtual float apply(float value) const = 0; - using Pointer = std::shared_ptr; using List = std::list; using Lambda = std::function; + using Factory = hifi::SimpleFactory; + virtual float apply(float value) const = 0; // Factory features virtual bool parseParameters(const QJsonArray& parameters) { return true; } - class Factory { - public: - - class Entry { - public: - virtual Filter* create() const = 0; - virtual const char* getName() const = 0; - - Entry() {}; - virtual ~Entry() {}; - }; - - template class ClassEntry : public Entry { - public: - virtual Filter* create() const { return (Filter*) new T(); } - virtual const char* getName() const { return T::getName(); }; - - ClassEntry() {}; - virtual ~ClassEntry() = default; - - class Builder { - public: - Builder() { - std::shared_ptr classEntry(new ClassEntry()); - Filter::getFactory().registerEntry(classEntry); - } - }; - }; - - using EntryMap = std::map>; - - void registerEntry(std::shared_ptr& entry) { - if (entry) { - _entries.insert(EntryMap::value_type(entry->getName(), entry)); - } - } - - Filter* create(const std::string& name) const { - const auto& entryIt = _entries.find(name); - if (entryIt != _entries.end()) { - return (*entryIt).second->create(); - } - return nullptr; - } - - protected: - EntryMap _entries; - }; - - static Filter::Pointer parse(const QJsonObject& json); + static Pointer parse(const QJsonObject& json); + static void registerBuilder(const QString& name, Factory::Builder builder); static Factory& getFactory() { return _factory; } protected: static Factory _factory; }; } -#define REGISTER_FILTER_CLASS(classEntry, className) \ - static const char* getName() { return className; } \ - using FactoryEntryBuilder = Filter::Factory::ClassEntry::Builder;\ - static FactoryEntryBuilder _factoryEntryBuilder; +#define REGISTER_FILTER_CLASS(classEntry) \ + private: \ + using Registrar = Filter::Factory::Registrar; \ + static Registrar _registrar; + +#define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \ + classEntry::Registrar classEntry::_registrar(className, Filter::getFactory()); namespace controller { @@ -123,8 +78,8 @@ namespace controller { }; class ScaleFilter : public Filter { + REGISTER_FILTER_CLASS(ScaleFilter); public: - REGISTER_FILTER_CLASS(ScaleFilter, "scale"); ScaleFilter() {} ScaleFilter(float scale): _scale(scale) {} @@ -138,8 +93,8 @@ namespace controller { }; class InvertFilter : public ScaleFilter { + REGISTER_FILTER_CLASS(InvertFilter); public: - REGISTER_FILTER_CLASS(InvertFilter, "invert"); InvertFilter() : ScaleFilter(-1.0f) {} virtual bool parseParameters(const QJsonArray& parameters) { return true; } @@ -148,8 +103,8 @@ namespace controller { }; class ClampFilter : public Filter { + REGISTER_FILTER_CLASS(ClampFilter); public: - REGISTER_FILTER_CLASS(ClampFilter, "clamp"); ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; virtual float apply(float value) const override { @@ -162,8 +117,8 @@ namespace controller { }; class DeadZoneFilter : public Filter { + REGISTER_FILTER_CLASS(DeadZoneFilter); public: - REGISTER_FILTER_CLASS(DeadZoneFilter, "deadZone"); DeadZoneFilter(float min = 0.0) : _min(min) {}; virtual float apply(float value) const override; @@ -173,8 +128,8 @@ namespace controller { }; class PulseFilter : public Filter { + REGISTER_FILTER_CLASS(PulseFilter); public: - REGISTER_FILTER_CLASS(PulseFilter, "pulse"); PulseFilter() {} PulseFilter(float interval) : _interval(interval) {} @@ -189,8 +144,8 @@ namespace controller { }; class ConstrainToIntegerFilter : public Filter { + REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); public: - REGISTER_FILTER_CLASS(ConstrainToIntegerFilter, "constrainToInteger"); ConstrainToIntegerFilter() {}; virtual float apply(float value) const override { @@ -200,8 +155,8 @@ namespace controller { }; class ConstrainToPositiveIntegerFilter : public Filter { + REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); public: - REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger"); ConstrainToPositiveIntegerFilter() {}; virtual float apply(float value) const override { diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/Mapping.h index ff988bf1b1..99328f310b 100644 --- a/libraries/controllers/src/controllers/Mapping.h +++ b/libraries/controllers/src/controllers/Mapping.h @@ -23,14 +23,12 @@ namespace controller { class Mapping { public: - // Map of source channels to route lists - using Map = std::map; using Pointer = std::shared_ptr; + using List = Route::List; Mapping(const QString& name) : name(name) {} - Map channelMappings; - + List routes; QString name; }; diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h index 8b0e70050f..f290799482 100644 --- a/libraries/controllers/src/controllers/Route.h +++ b/libraries/controllers/src/controllers/Route.h @@ -12,8 +12,7 @@ #include "Endpoint.h" #include "Filter.h" - -class QJsonObject; +#include "Conditional.h" namespace controller { /* @@ -23,6 +22,7 @@ namespace controller { public: Endpoint::Pointer source; Endpoint::Pointer destination; + Conditional::Pointer conditional; Filter::List filters; using Pointer = std::shared_ptr; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 0c3a43bd34..757f680163 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -8,6 +8,8 @@ #include "UserInputMapper.h" +#include + #include #include @@ -208,17 +210,46 @@ void UserInputMapper::registerDevice(InputDevice* device) { auto mapping = loadMapping(device->getDefaultMappingConfig()); if (mapping) { _mappingsByDevice[deviceID] = mapping; - for (const auto& entry : mapping->channelMappings) { - const auto& source = entry.first; - const auto& routes = entry.second; - auto& list = _defaultMapping->channelMappings[source]; - list.insert(list.end(), routes.begin(), routes.end()); - } + auto& defaultRoutes = _defaultMapping->routes; + + // New routes for a device get injected IN FRONT of existing routes. Routes + // are processed in order so this ensures that the standard -> action processing + // takes place after all of the hardware -> standard or hardware -> action processing + // because standard -> action is the first set of routes added. + defaultRoutes.insert(defaultRoutes.begin(), mapping->routes.begin(), mapping->routes.end()); } emit hardwareChanged(); } +// FIXME remove the associated device mappings +void UserInputMapper::removeDevice(int deviceID) { + auto proxyEntry = _registeredDevices.find(deviceID); + if (_registeredDevices.end() == proxyEntry) { + qCWarning(controllers) << "Attempted to remove unknown device " << deviceID; + return; + } + auto proxy = proxyEntry->second; + auto mappingsEntry = _mappingsByDevice.find(deviceID); + if (_mappingsByDevice.end() != mappingsEntry) { + const auto& mapping = mappingsEntry->second; + const auto& deviceRoutes = mapping->routes; + std::set routeSet(deviceRoutes.begin(), deviceRoutes.end()); + + auto& defaultRoutes = _defaultMapping->routes; + std::remove_if(defaultRoutes.begin(), defaultRoutes.end(), [&](Route::Pointer route)->bool { + return routeSet.count(route) != 0; + }); + + _mappingsByDevice.erase(mappingsEntry); + } + + _registeredDevices.erase(proxyEntry); + + emit hardwareChanged(); +} + + DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { auto device = _registeredDevices.find(input.getDevice()); if (device != _registeredDevices.end()) { @@ -290,11 +321,6 @@ Input UserInputMapper::findDeviceInput(const QString& inputName) const { return Input::INVALID_INPUT; } -// FIXME remove the associated device mappings -void UserInputMapper::removeDevice(int device) { - _registeredDevices.erase(device); -} - void fixBisectedAxis(float& full, float& negative, float& positive) { full = full + (negative * -1.0f) + positive; negative = full >= 0.0f ? 0.0f : full * -1.0f; @@ -470,12 +496,6 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { return Input(STANDARD_DEVICE, pose, ChannelType::POSE); } -enum Pass { - HARDWARE_PASS, - STANDARD_PASS, - NUM_PASSES -}; - void UserInputMapper::update() { static auto deviceNames = getDeviceNames(); _overrideValues.clear(); @@ -485,66 +505,58 @@ void UserInputMapper::update() { // Now process the current values for each level of the stack for (auto& mapping : _activeMappings) { - for (int pass = HARDWARE_PASS; pass < NUM_PASSES; ++pass) { - for (const auto& mappingEntry : mapping->channelMappings) { - const auto& source = mappingEntry.first; - if (_inputsByEndpoint.count(source)) { - auto sourceInput = _inputsByEndpoint[source]; - if ((sourceInput.device == STANDARD_DEVICE) ^ (pass == STANDARD_PASS)) { - continue; - } - } + for (const auto& route : mapping->routes) { + const auto& source = route->source; + // 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 + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // I press the button. The exception is if I'm wiring a control back to itself + // in order to adjust my interface, like inverting the Y axis on an analog stick + if (readEndpoints.count(source)) { + continue; + } - // 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 - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when - // I press the button. The exception is if I'm wiring a control back to itself - // in order to adjust my interface, like inverting the Y axis on an analog stick - if (readEndpoints.count(source)) { + const auto& destination = route->destination; + // THis could happen if the route destination failed to create + // FIXME: Maybe do not create the route if the destination failed and avoid this case ? + if (!destination) { + continue; + } + + if (writtenEndpoints.count(destination)) { + continue; + } + + if (route->conditional) { + if (!route->conditional->satisfied()) { continue; } + } - // Apply the value to all the routes - const auto& routes = mappingEntry.second; + // Standard controller destinations can only be can only be used once. + if (getStandardDeviceID() == destination->getInput().getDevice()) { + writtenEndpoints.insert(destination); + } - for (const auto& route : routes) { - const auto& destination = route->destination; - // THis could happen if the route destination failed to create - // FIXME: Maybe do not create the route if the destination failed and avoid this case ? - if (!destination) { - continue; - } + // Only consume the input if the route isn't a loopback. + // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` + bool loopback = source == destination; + if (!loopback) { + readEndpoints.insert(source); + } - if (writtenEndpoints.count(destination)) { - continue; - } + // Fetch the value, may have been overriden by previous loopback routes + float value = getValue(source); - // Standard controller destinations can only be can only be used once. - if (getStandardDeviceID() == destination->getInput().getDevice()) { - writtenEndpoints.insert(destination); - } + // Apply each of the filters. + for (const auto& filter : route->filters) { + value = filter->apply(value); + } - // Only consume the input if the route isn't a loopback. - // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` - bool loopback = source == destination; - if (!loopback) { - readEndpoints.insert(source); - } - - // Fetch the value, may have been overriden by previous loopback routes - float value = getValue(source); - - // Apply each of the filters. - for (const auto& filter : route->filters) { - value = filter->apply(value); - } - - if (loopback) { - _overrideValues[source] = value; - } else { - destination->apply(value, 0, source); - } - } + if (loopback) { + _overrideValues[source] = value; + } else { + destination->apply(value, 0, source); } } } @@ -693,6 +705,7 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { const QString JSON_NAME = QStringLiteral("name"); const QString JSON_CHANNELS = QStringLiteral("channels"); const QString JSON_CHANNEL_FROM = QStringLiteral("from"); +const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); const QString JSON_CHANNEL_TO = QStringLiteral("to"); const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); @@ -707,6 +720,20 @@ Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { return Endpoint::Pointer(); } +Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { + if (value.isString()) { + auto input = findDeviceInput(value.toString()); + auto endpoint = endpointFor(input); + if (!endpoint) { + return Conditional::Pointer(); + } + + return std::make_shared(endpoint); + } + + return Conditional::parse(value); +} + Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { if (!value.isObject()) { return Route::Pointer(); @@ -725,6 +752,15 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { return Route::Pointer(); } + if (obj.contains(JSON_CHANNEL_WHEN)) { + auto when = parseConditional(obj[JSON_CHANNEL_WHEN]); + if (!when) { + qWarning() << "Invalid route conditional " << obj[JSON_CHANNEL_TO]; + return Route::Pointer(); + } + result->conditional = when; + } + const auto& filtersValue = obj[JSON_CHANNEL_FILTERS]; if (filtersValue.isArray()) { auto filtersArray = filtersValue.toArray(); @@ -752,16 +788,14 @@ Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { auto obj = json.toObject(); auto mapping = std::make_shared("default"); mapping->name = obj[JSON_NAME].toString(); - mapping->channelMappings.clear(); const auto& jsonChannels = obj[JSON_CHANNELS].toArray(); - Mapping::Map map; for (const auto& channelIt : jsonChannels) { Route::Pointer route = parseRoute(channelIt); if (!route) { qWarning() << "Couldn't parse route"; continue; } - mapping->channelMappings[route->source].push_back(route); + mapping->routes.push_back(route); } return mapping; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 1c1a9506df..5d5895fa7b 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -142,6 +142,7 @@ namespace controller { Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); Mapping::Pointer parseMapping(const QJsonValue& json); Route::Pointer parseRoute(const QJsonValue& value); + Conditional::Pointer parseConditional(const QJsonValue& value); Endpoint::Pointer parseEndpoint(const QJsonValue& value); InputToEndpointMap _endpointsByInput; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index d60032cb43..e76f55534f 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -41,7 +41,7 @@ void RouteBuilderProxy::to(const QScriptValue& destination) { void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { auto sourceEndpoint = _route->source; _route->destination = destination; - _mapping->channelMappings[sourceEndpoint].push_back(_route); + _mapping->routes.push_back(_route); deleteLater(); } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index f5f8b47a90..ff6c0ce2de 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -183,24 +183,24 @@ void KeyboardMouseDevice::buildDeviceProxy(controller::DeviceProxy::Pointer prox availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); - availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "Left Mouse Click")); - availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "Middle Mouse Click")); - availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "Right Mouse Click")); + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick")); + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick")); + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up")); - availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp")); + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "MouseWheelDown")); - availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right")); - availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left")); - availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up")); - availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "TouchpadRight")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp")); + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown")); return availableInputs; }; diff --git a/libraries/shared/src/shared/Factory.h b/libraries/shared/src/shared/Factory.h new file mode 100644 index 0000000000..6f1da6644b --- /dev/null +++ b/libraries/shared/src/shared/Factory.h @@ -0,0 +1,51 @@ +// +// Created by Bradley Austin Davis 2015/10/09 +// 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_Shared_Factory_h +#define hifi_Shared_Factory_h + +#include +#include +#include + +namespace hifi { + + template + class SimpleFactory { + public: + using Pointer = std::shared_ptr; + using Builder = std::function; + using BuilderMap = std::map; + + void registerBuilder(const Key& name, Builder builder) { + // FIXME don't allow name collisions + _builders[name] = builder; + } + + Pointer create(const Key& name) const { + const auto& entryIt = _builders.find(name); + if (entryIt != _builders.end()) { + return (*entryIt).second(); + } + return Pointer(); + } + + template + class Registrar { + public: + Registrar(const Key& name, SimpleFactory& factory) { + factory.registerBuilder(name, [] { return std::make_shared(); }); + } + }; + protected: + BuilderMap _builders; + }; +} + +#endif From 3d2f00c609fc5ee48afcbaec5ffe56b4e0acc11d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 20 Oct 2015 17:01:45 -0700 Subject: [PATCH 073/232] Cleaner intgerface, including cleanup. --- libraries/animation/src/Rig.cpp | 56 +++++++++++++++++++++++---------- libraries/animation/src/Rig.h | 7 +++-- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2fd7364434..215e0095c0 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -604,6 +605,42 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastPosition = worldPosition; } +void Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { + _stateHandlers = handler; +} +void Rig::removeAnimationStateHandler(QScriptValue handler) { + _stateHandlersResultsToRemove = _stateHandlersResults; + _stateHandlers = _stateHandlersResults = QScriptValue(); +} +void Rig::cleanupAnimationStateHandler() { + if (!_stateHandlersResultsToRemove.isValid()) { + return; + } + QScriptValueIterator property(_stateHandlersResultsToRemove); + while (property.hasNext()) { + property.next(); + _animVars.unset(property.name()); + } + _stateHandlersResultsToRemove = QScriptValue(); +} +void Rig::updateAnimationStateHandlers() { + if (!_stateHandlers.isValid()) { + return; + } + // TODO: iterate multiple handlers, but with one shared arg. + // TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) + // TODO: check QScriptEngine::hasUncaughtException() + // TODO: call asynchronously (through a signal on script), so that each script is single threaded, and so we never block here. + // This will require inboundMaps to be kept in the list of per-handler data. + QScriptEngine* engine = _stateHandlers.engine(); + QScriptValue outboundMap = _animVars.animVariantMapToScriptValue(engine); + QScriptValueList args; + args << outboundMap; + _stateHandlersResults = _stateHandlers.call(QScriptValue(), args); + _animVars.animVariantMapFromScriptValue(_stateHandlersResults); + //qCDebug(animation) << _animVars.lookup("foo", QString("not set")); +} + void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { if (_enableAnimGraph) { @@ -611,20 +648,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { return; } - if (_stateHandlers.isValid()) { - // TODO: iterate multiple handlers, but with one shared arg. - // TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) - // TODO: check QScriptEngine::hasUncaughtException() - // TODO: call asynchronously (through a signal on script), so that each script is single threaded, and so we never block here. - // This will require inboundMaps to be kept in the list of per-handler data. - QScriptEngine* engine = _stateHandlers.engine(); - QScriptValue outboundMap = _animVars.animVariantMapToScriptValue(engine); - QScriptValueList args; - args << outboundMap; - _stateHandlersResults = _stateHandlers.call(QScriptValue(), args); - _animVars.animVariantMapFromScriptValue(_stateHandlersResults); - //qCDebug(animation) << _animVars.lookup("foo", QString("not set")); - } + updateAnimationStateHandlers(); // evaluate the animation AnimNode::Triggers triggersOut; AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); @@ -1201,9 +1225,7 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { - if (!_stateHandlersResults.property("rightHandPosition", QScriptValue::ResolveLocal).isValid()) { - _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); - } + _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 89fde9002f..3f68deef1a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -200,12 +200,14 @@ public: AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) - void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _stateHandlers = handler; } - void removeAnimationStateHandler(QScriptValue handler) { _stateHandlers = QScriptValue(); } + void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); + void removeAnimationStateHandler(QScriptValue handler); + void cleanupAnimationStateHandler(); bool getModelOffset(glm::vec3& modelOffsetOut) const; protected: + void updateAnimationStateHandlers(); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); @@ -248,6 +250,7 @@ public: private: QScriptValue _stateHandlers {}; QScriptValue _stateHandlersResults {}; + QScriptValue _stateHandlersResultsToRemove {}; }; #endif /* defined(__hifi__Rig__) */ From 574a51e83140e7784e7ee1e7ab37d6e23bd4df5f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 20 Oct 2015 17:02:16 -0700 Subject: [PATCH 074/232] Use cleaner interface. --- interface/src/avatar/SkeletonModel.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 28c7941c52..5c98d4cd9b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -111,11 +111,8 @@ static const PalmData* getPalmWithIndex(Hand* hand, int index) { const float PALM_PRIORITY = DEFAULT_PRIORITY; // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { - if (_owningAvatar->isMyAvatar()) { - _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); - } - Model::updateRig(deltaTime, parentTransform); Head* head = _owningAvatar->getHead(); + _rig->cleanupAnimationStateHandler(); if (_owningAvatar->isMyAvatar()) { MyAvatar* myAvatar = static_cast(_owningAvatar); const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -191,6 +188,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); + _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); + } else { // This is a little more work than we really want. // @@ -212,6 +211,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { getTranslation(), getRotation(), head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition()); } + Model::updateRig(deltaTime, parentTransform); } void SkeletonModel::updateAttitude() { From 6f7719e9e9cb021545f807129ad5e8c8a4eb2b8a Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 20 Oct 2015 17:44:24 -0700 Subject: [PATCH 075/232] Finally getting the merge to work --- libraries/controllers/src/controllers/Actions.h | 3 ++- .../src/controllers/ScriptingInterface.cpp | 14 ++++++++++++++ .../src/controllers/UserInputMapper.cpp | 1 - .../controllers/src/controllers/UserInputMapper.h | 9 ++++++--- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 2b581eb706..77a772de9e 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -71,7 +71,8 @@ enum class Action { NUM_ACTIONS, }; -int toInt(Action action) { return static_cast(action); } +template +int toInt(T enumValue) { return static_cast(enumValue); } class ActionsDevice : public QObject, public InputDevice { Q_OBJECT diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 37c3035535..40c65549a8 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -71,6 +71,16 @@ controller::ScriptingInterface::ScriptingInterface() { namespace controller { + QObject* ScriptingInterface::newMapping(const QString& mappingName) { + auto userInputMapper = DependencyManager::get(); + return new MappingBuilderProxy(*userInputMapper, userInputMapper->newMapping(mappingName)); + } + + void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->enableMapping(mappingName, enable); + } + float ScriptingInterface::getValue(const int& source) const { auto userInputMapper = DependencyManager::get(); return userInputMapper->getValue(Input((uint32_t)source)); @@ -88,6 +98,10 @@ namespace controller { auto userInputMapper = DependencyManager::get(); return userInputMapper->getPose(Input((uint32_t)source)); } + + Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { + return getPoseValue(Input(device, source, ChannelType::POSE).getID()); + } //bool ScriptingInterface::isPrimaryButtonPressed() const { // return isButtonPressed(StandardButtonChannel::A); diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 8dfdd33eba..6c530020a9 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -31,7 +31,6 @@ controller::UserInputMapper::UserInputMapper() { _standardController = std::make_shared(); registerDevice(new ActionsDevice()); registerDevice(_standardController.get()); - assignDefaulActionScales(); } namespace controller { diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index d5b2e4e282..e9b6c59596 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -32,6 +32,10 @@ #include "Actions.h" namespace controller { + + class RouteBuilderProxy; + class MappingBuilderProxy; + class UserInputMapper : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -81,7 +85,6 @@ namespace controller { Pose getPoseState(Action action) const { return _poseStates[toInt(action)]; } int findAction(const QString& actionName) const; QVector getActionNames() const; - void assignDefaulActionScales(); void setActionState(Action action, float value) { _externalActionStates[toInt(action)] = value; } void deltaActionState(Action action, float delta) { _externalActionStates[toInt(action)] += delta; } @@ -139,8 +142,8 @@ namespace controller { float getValue(const Endpoint::Pointer& endpoint) const; Pose getPose(const Endpoint::Pointer& endpoint) const; - friend class ::controller::RouteBuilderProxy; - friend class ::controller::MappingBuilderProxy; + friend class RouteBuilderProxy; + friend class MappingBuilderProxy; Endpoint::Pointer endpointFor(const QJSValue& endpoint); Endpoint::Pointer endpointFor(const QScriptValue& endpoint); Endpoint::Pointer endpointFor(const Input& endpoint) const; From da26d0dee159b241932ee2eaadc5921386ce095a Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 20 Oct 2015 18:19:44 -0700 Subject: [PATCH 076/232] Adding the hand poses channels in the controller mapping files --- interface/resources/controllers/hydra.json | 7 +++++-- interface/resources/controllers/standard.json | 4 +++- libraries/controllers/src/controllers/UserInputMapper.h | 1 - 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index 25c8db61cb..c20d54b7c1 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -23,6 +23,9 @@ { "from": "Hydra.R1", "to": "Standard.X" }, { "from": "Hydra.R2", "to": "Standard.A" }, { "from": "Hydra.R3", "to": "Standard.B" }, - { "from": "Hydra.R4", "to": "Standard.Y" } - ] + { "from": "Hydra.R4", "to": "Standard.Y" }, + + { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Hydra.RightHand", "to": "Standard.RightHand" } + ] } diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 364d24ae16..b662e5394d 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -36,6 +36,8 @@ "from": "Standard.LT", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.1 } ] - } + }, + { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, + { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } ] } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index e9b6c59596..7561ba84af 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -174,6 +174,5 @@ Q_DECLARE_METATYPE(QVector) // Cheating. using UserInputMapper = controller::UserInputMapper; -//>>>>>>> 9c031b6bef988f123cb955c81299395386ec488c #endif // hifi_UserInputMapper_h From 053aca2e3695ebb8aa9ffbeae1c1886f5e3a7813 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 20 Oct 2015 21:12:46 -0700 Subject: [PATCH 077/232] fixing the cast bug on osx --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e23e19652..fcb90a96cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -630,7 +630,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(userInputMapper.data(), &UserInputMapper::actionEvent, _controllerScriptingInterface, &ControllerScriptingInterface::actionEvent); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { if (state) { - switch (action) { + switch (controller::Action(action)) { case controller::Action::TOGGLE_MUTE: DependencyManager::get()->toggleMute(); break; From a124d3b4332205436b73997079600f52c3dd03c0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 22:00:16 -0700 Subject: [PATCH 078/232] Moving to InputEndpoint, fixing build problem --- interface/src/Application.cpp | 8 ++------ libraries/controllers/src/controllers/UserInputMapper.cpp | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e23e19652..4418f94b3a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -629,12 +629,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : auto userInputMapper = DependencyManager::get(); connect(userInputMapper.data(), &UserInputMapper::actionEvent, _controllerScriptingInterface, &ControllerScriptingInterface::actionEvent); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { - if (state) { - switch (action) { - case controller::Action::TOGGLE_MUTE: - DependencyManager::get()->toggleMute(); - break; - } + if (state && action == toInt(controller::Action::TOGGLE_MUTE)) { + DependencyManager::get()->toggleMute(); } }); diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 5d93150aed..74b3db0d57 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -255,9 +255,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { } else if (input.device == ACTIONS_DEVICE) { endpoint = std::make_shared(input); } else { - endpoint = std::make_shared([=] { - return proxy->getValue(input, 0); - }); + endpoint = std::make_shared(input); } _inputsByEndpoint[endpoint] = input; _endpointsByInput[input] = endpoint; From f0edc302bf351a8be5b22d6d2c60c10e3886ee8f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 22:06:08 -0700 Subject: [PATCH 079/232] Fixing filters creation --- libraries/controllers/src/controllers/UserInputMapper.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 74b3db0d57..0c47a2dce8 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -845,10 +845,15 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { } const auto& filtersValue = obj[JSON_CHANNEL_FILTERS]; + // FIXME support strings for filters with no parameters, both in the array and at the top level... + // i.e. + // { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : "invert" }, + // and + // { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : [ "invert", "constrainToInteger" ] }, if (filtersValue.isArray()) { auto filtersArray = filtersValue.toArray(); for (auto filterValue : filtersArray) { - if (filterValue.isObject()) { + if (!filterValue.isObject()) { qWarning() << "Invalid filter " << filterValue; return Route::Pointer(); } From ac2a60befb73dd11fe4d04a3cf90e8e8a6be3f03 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 10:57:41 -0700 Subject: [PATCH 080/232] Adding primary/secondary thumb abstraction --- .../src/controllers/StandardController.cpp | 81 ++++++++++--------- .../src/controllers/StandardControls.h | 11 +++ 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index e9a2e71973..061fc4ea56 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -40,59 +40,66 @@ void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) { proxy->getAvailabeInputs = [this] () -> QVector { QVector availableInputs; // Buttons - availableInputs.append(Input::NamedPair(makeInput(controller::A), "A")); - availableInputs.append(Input::NamedPair(makeInput(controller::B), "B")); - availableInputs.append(Input::NamedPair(makeInput(controller::X), "X")); - availableInputs.append(Input::NamedPair(makeInput(controller::Y), "Y")); + availableInputs.append(makePair(A, "A")); + availableInputs.append(makePair(B, "B")); + availableInputs.append(makePair(X, "X")); + availableInputs.append(makePair(Y, "Y")); // DPad - availableInputs.append(Input::NamedPair(makeInput(controller::DU), "DU")); - availableInputs.append(Input::NamedPair(makeInput(controller::DD), "DD")); - availableInputs.append(Input::NamedPair(makeInput(controller::DL), "DL")); - availableInputs.append(Input::NamedPair(makeInput(controller::DR), "DR")); + availableInputs.append(makePair(DU, "DU")); + availableInputs.append(makePair(DD, "DD")); + availableInputs.append(makePair(DL, "DL")); + availableInputs.append(makePair(DR, "DR")); // Bumpers - availableInputs.append(Input::NamedPair(makeInput(controller::LB), "LB")); - availableInputs.append(Input::NamedPair(makeInput(controller::RB), "RB")); + availableInputs.append(makePair(LB, "LB")); + availableInputs.append(makePair(RB, "RB")); // Stick press - availableInputs.append(Input::NamedPair(makeInput(controller::LS), "LS")); - availableInputs.append(Input::NamedPair(makeInput(controller::RS), "RS")); + availableInputs.append(makePair(LS, "LS")); + availableInputs.append(makePair(RS, "RS")); // Center buttons - availableInputs.append(Input::NamedPair(makeInput(controller::START), "Start")); - availableInputs.append(Input::NamedPair(makeInput(controller::BACK), "Back")); + availableInputs.append(makePair(START, "Start")); + availableInputs.append(makePair(BACK, "Back")); // Analog sticks - availableInputs.append(Input::NamedPair(makeInput(controller::LY), "LY")); - availableInputs.append(Input::NamedPair(makeInput(controller::LX), "LX")); - availableInputs.append(Input::NamedPair(makeInput(controller::RY), "RY")); - availableInputs.append(Input::NamedPair(makeInput(controller::RX), "RX")); + availableInputs.append(makePair(LY, "LY")); + availableInputs.append(makePair(LX, "LX")); + availableInputs.append(makePair(RY, "RY")); + availableInputs.append(makePair(RX, "RX")); // Triggers - availableInputs.append(Input::NamedPair(makeInput(controller::LT), "LT")); - availableInputs.append(Input::NamedPair(makeInput(controller::RT), "RT")); + availableInputs.append(makePair(LT, "LT")); + availableInputs.append(makePair(RT, "RT")); // Poses - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::LEFT_HAND), "LeftHand")); - availableInputs.append(UserInputMapper::InputPair(makeInput(controller::RIGHT_HAND), "RightHand")); + availableInputs.append(makePair(LEFT_HAND, "LeftHand")); + availableInputs.append(makePair(RIGHT_HAND, "RightHand")); // Aliases, PlayStation style names - availableInputs.append(Input::NamedPair(makeInput(controller::LB), "L1")); - availableInputs.append(Input::NamedPair(makeInput(controller::RB), "R1")); - availableInputs.append(Input::NamedPair(makeInput(controller::LT), "L2")); - availableInputs.append(Input::NamedPair(makeInput(controller::RT), "R2")); - availableInputs.append(Input::NamedPair(makeInput(controller::LS), "L3")); - availableInputs.append(Input::NamedPair(makeInput(controller::RS), "R3")); - availableInputs.append(Input::NamedPair(makeInput(controller::BACK), "Select")); - availableInputs.append(Input::NamedPair(makeInput(controller::A), "Cross")); - availableInputs.append(Input::NamedPair(makeInput(controller::B), "Circle")); - availableInputs.append(Input::NamedPair(makeInput(controller::X), "Square")); - availableInputs.append(Input::NamedPair(makeInput(controller::Y), "Triangle")); - availableInputs.append(Input::NamedPair(makeInput(controller::DU), "Up")); - availableInputs.append(Input::NamedPair(makeInput(controller::DD), "Down")); - availableInputs.append(Input::NamedPair(makeInput(controller::DL), "Left")); - availableInputs.append(Input::NamedPair(makeInput(controller::DR), "Right")); + availableInputs.append(makePair(LB, "L1")); + availableInputs.append(makePair(RB, "R1")); + availableInputs.append(makePair(LT, "L2")); + availableInputs.append(makePair(RT, "R2")); + availableInputs.append(makePair(LS, "L3")); + availableInputs.append(makePair(RS, "R3")); + availableInputs.append(makePair(BACK, "Select")); + availableInputs.append(makePair(A, "Cross")); + availableInputs.append(makePair(B, "Circle")); + availableInputs.append(makePair(X, "Square")); + availableInputs.append(makePair(Y, "Triangle")); + availableInputs.append(makePair(DU, "Up")); + availableInputs.append(makePair(DD, "Down")); + availableInputs.append(makePair(DL, "Left")); + availableInputs.append(makePair(DR, "Right")); + + + availableInputs.append(makePair(LeftPrimaryThumb, "LeftPrimaryThumb")); + availableInputs.append(makePair(LeftSecondaryThumb, "LeftSecondaryThumb")); + availableInputs.append(makePair(RightPrimaryThumb, "RightPrimaryThumb")); + availableInputs.append(makePair(RightSecondaryThumb, "RightSecondaryThumb")); + return availableInputs; }; } diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index dc39a8bbeb..26644e2f38 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -16,21 +16,32 @@ namespace controller { B, X, Y, + // Center buttons BACK, GUIDE, START, + // Stick press LS, RS, + // Bumper press LB, RB, + // DPad DU, DD, DL, DR, + + // These don't map to SDL types + LeftPrimaryThumb, + LeftSecondaryThumb, + RightPrimaryThumb, + RightSecondaryThumb, + NUM_STANDARD_BUTTONS }; From 84e2ace0ead11d7de1e8a60a60dff127084a4bc4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 12:57:48 -0700 Subject: [PATCH 081/232] Prevent crash on connecting gamepad --- libraries/input-plugins/src/input-plugins/SDL2Manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index b052162c77..54197b1a70 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -129,6 +129,7 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { if (!_openJoysticks.contains(id)) { // Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); Joystick* joystick = new Joystick(id, controller); + _openJoysticks[id] = joystick; userInputMapper->registerDevice(joystick); emit joystickAdded(joystick); } From 3eedfd369e7c02866d8d76e6228c7ee8aac7afed Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 14:01:18 -0700 Subject: [PATCH 082/232] expose the Pose structure to JS --- .../controllers/src/controllers/Pose.cpp | 19 +++++++++++++++++++ libraries/controllers/src/controllers/Pose.h | 8 ++++++-- .../src/controllers/UserInputMapper.cpp | 3 +++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 112e6e01a9..9cb33bc777 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -6,6 +6,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include + +#include + #include "Pose.h" namespace controller { @@ -25,6 +30,20 @@ namespace controller { velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity(); } + QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) { + QScriptValue obj = engine->newObject(); + obj.setProperty("translation", vec3toScriptValue(engine, pose.translation)); + obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation)); + obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity)); + obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity)); + obj.setProperty("valid", pose.valid); + + return obj; + } + + void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { + // nothing for now... + } } diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 9243ceb734..b792b0e901 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -11,6 +11,7 @@ #ifndef hifi_controllers_Pose_h #define hifi_controllers_Pose_h +#include #include namespace controller { @@ -35,9 +36,12 @@ namespace controller { quat getRotation() const { return rotation; } vec3 getVelocity() const { return velocity; } quat getAngularVelocity() const { return angularVelocity; } + + static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); + static void fromScriptValue(const QScriptValue& object, Pose& event); }; - - } +//Q_DECLARE_METATYPE(controller::Pose); + #endif diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 6ef64fc784..b9833a1f6b 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -495,6 +495,7 @@ void UserInputMapper::assignDefaulActionScales() { static int actionMetaTypeId = qRegisterMetaType(); static int inputMetaTypeId = qRegisterMetaType(); static int inputPairMetaTypeId = qRegisterMetaType(); +static int poseMetaTypeId = qRegisterMetaType("Pose"); QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); @@ -547,6 +548,8 @@ void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); + + qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue); } Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { From ffd277d4d5d75a1a59fea8f6505b3449c9b00aad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 14:31:17 -0700 Subject: [PATCH 083/232] fix EntityItemID dependency --- libraries/entities/src/EntityItemID.cpp | 16 ++++++++++++++++ libraries/entities/src/EntityItemID.h | 1 + libraries/shared/src/RegisteredMetaTypes.cpp | 15 --------------- libraries/shared/src/RegisteredMetaTypes.h | 2 -- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityItemID.cpp b/libraries/entities/src/EntityItemID.cpp index 36664f5457..1462a4ef88 100644 --- a/libraries/entities/src/EntityItemID.cpp +++ b/libraries/entities/src/EntityItemID.cpp @@ -52,3 +52,19 @@ QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& id) { quuidFromScriptValue(object, id); } + +QVector qVectorEntityItemIDFromScriptValue(const QScriptValue& array) { + if (!array.isArray()) { + return QVector(); + } + QVector newVector; + int length = array.property("length").toInteger(); + newVector.reserve(length); + for (int i = 0; i < length; i++) { + QString uuidAsString = array.property(i).toString(); + EntityItemID fromString(uuidAsString); + newVector << fromString; + } + return newVector; +} + diff --git a/libraries/entities/src/EntityItemID.h b/libraries/entities/src/EntityItemID.h index 765082ef0b..41a11147f8 100644 --- a/libraries/entities/src/EntityItemID.h +++ b/libraries/entities/src/EntityItemID.h @@ -43,5 +43,6 @@ Q_DECLARE_METATYPE(EntityItemID); Q_DECLARE_METATYPE(QVector); QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties); void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties); +QVector qVectorEntityItemIDFromScriptValue(const QScriptValue& array); #endif // hifi_EntityItemID_h diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 016c99a8dc..008ac238d5 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -118,21 +118,6 @@ QVector qVectorQUuidFromScriptValue(const QScriptValue& array) { return newVector; } -QVector qVectorEntityItemIDFromScriptValue(const QScriptValue& array) { - if (!array.isArray()) { - return QVector(); - } - QVector newVector; - int length = array.property("length").toInteger(); - newVector.reserve(length); - for (int i = 0; i < length; i++) { - QString uuidAsString = array.property(i).toString(); - EntityItemID fromString(uuidAsString); - newVector << fromString; - } - return newVector; -} - QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector& vector) { QScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 8e53f0ee37..cd1e3b0d3e 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -14,7 +14,6 @@ #include #include -#include "../../entities/src/EntityItemID.h" #include #include @@ -67,7 +66,6 @@ void qVectorFloatFromScriptValue(const QScriptValue& array, QVector& vect QVector qVectorFloatFromScriptValue(const QScriptValue& array); QVector qVectorQUuidFromScriptValue(const QScriptValue& array); -QVector qVectorEntityItemIDFromScriptValue(const QScriptValue& array); class PickRay { public: From 6bd1e593059cf45740cb4dadab4681c57744fe95 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 14:31:44 -0700 Subject: [PATCH 084/232] fix CR feedback --- libraries/controllers/src/controllers/Pose.cpp | 4 ++-- libraries/controllers/src/controllers/Pose.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 9cb33bc777..2281fc98ff 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -6,8 +6,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include +#include +#include #include diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index b792b0e901..3d0f716087 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -11,7 +11,9 @@ #ifndef hifi_controllers_Pose_h #define hifi_controllers_Pose_h -#include +class QScriptEngine; +class QScriptValue; + #include namespace controller { From b9b03bd842e03a60ba447c4039c2583b08bc1e33 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 14:31:22 -0700 Subject: [PATCH 085/232] Working on conditional and filter parsing --- interface/resources/controllers/hydra.json | 4 +- .../resources/controllers/standard-old.json | 43 ++ interface/resources/controllers/standard.json | 31 +- interface/resources/qml/TestControllers.qml | 53 +-- .../resources/qml/controller/AnalogButton.qml | 2 +- .../resources/qml/controller/AnalogStick.qml | 6 +- .../resources/qml/controller/ToggleButton.qml | 12 +- interface/resources/qml/controller/Xbox.qml | 15 + .../src/controllers/Conditional.cpp | 10 - .../controllers/src/controllers/Conditional.h | 10 - .../controllers/src/controllers/Endpoint.h | 25 +- libraries/controllers/src/controllers/Input.h | 1 + libraries/controllers/src/controllers/Pose.h | 1 + .../src/controllers/UserInputMapper.cpp | 429 +++++++++++++----- .../src/controllers/UserInputMapper.h | 22 +- 15 files changed, 453 insertions(+), 211 deletions(-) create mode 100644 interface/resources/controllers/standard-old.json diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index c20d54b7c1..20d954932a 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -1,10 +1,10 @@ { "name": "Hydra to Standard", "channels": [ - { "from": "Hydra.LY", "to": "Standard.LY" }, + { "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" }, { "from": "Hydra.LX", "to": "Standard.LX" }, { "from": "Hydra.LT", "to": "Standard.LT" }, - { "from": "Hydra.RY", "to": "Standard.RY" }, + { "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" }, { "from": "Hydra.RX", "to": "Standard.RX" }, { "from": "Hydra.RT", "to": "Standard.RT" }, diff --git a/interface/resources/controllers/standard-old.json b/interface/resources/controllers/standard-old.json new file mode 100644 index 0000000000..b662e5394d --- /dev/null +++ b/interface/resources/controllers/standard-old.json @@ -0,0 +1,43 @@ +{ + "name": "Standard to Action", + "channels": [ + { "from": "Standard.LY", "to": "Actions.TranslateZ" }, + { "from": "Standard.LX", "to": "Actions.TranslateX" }, + { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RY", "to": "Actions.Pitch" }, + { + "from": "Standard.DU", + "to": "Actions.LONGITUDINAL_FORWARD", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { + "from": "Standard.DD", + "to": "Actions.LONGITUDINAL_BACKWARD", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { + "from": "Standard.DR", + "to": "Actions.LATERAL_RIGHT", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { + "from": "Standard.DL", + "to": "Actions.LATERAL_LEFT", + "filters": [ { "type": "scale", "scale": 0.5 } ] + }, + { "from": "Standard.Y", "to": "Actions.VERTICAL_UP" }, + { "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" }, + { + "from": "Standard.RT", + "to": "Actions.BOOM_IN", + "filters": [ { "type": "scale", "scale": 0.1 } ] + }, + { + "from": "Standard.LT", + "to": "Actions.BOOM_OUT", + "filters": [ { "type": "scale", "scale": 0.1 } ] + }, + { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, + { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } + ] +} diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index b662e5394d..ef6668ec07 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -5,38 +5,25 @@ { "from": "Standard.LX", "to": "Actions.TranslateX" }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, - { - "from": "Standard.DU", - "to": "Actions.LONGITUDINAL_FORWARD", - "filters": [ { "type": "scale", "scale": 0.5 } ] - }, - { - "from": "Standard.DD", - "to": "Actions.LONGITUDINAL_BACKWARD", - "filters": [ { "type": "scale", "scale": 0.5 } ] - }, - { - "from": "Standard.DR", - "to": "Actions.LATERAL_RIGHT", - "filters": [ { "type": "scale", "scale": 0.5 } ] - }, - { - "from": "Standard.DL", - "to": "Actions.LATERAL_LEFT", - "filters": [ { "type": "scale", "scale": 0.5 } ] - }, - { "from": "Standard.Y", "to": "Actions.VERTICAL_UP" }, - { "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" }, + + { "from": [ "Standard.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, + { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, + + { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, + { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, + { "from": "Standard.RT", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.1 } ] }, + { "from": "Standard.LT", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.1 } ] }, + { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } ] diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index e409b7a4a4..71a836f2e4 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -183,32 +183,33 @@ HifiControls.VrDialog { Xbox { device: root.xbox; label: "XBox"; width: 360 } Hydra { device: root.hydra; width: 360 } } -// Row { -// spacing: 8 -// ScrollingGraph { -// controlId: Controller.Actions.Yaw -// label: "Yaw" -// min: -3.0 -// max: 3.0 -// size: 128 -// } -// -// ScrollingGraph { -// controlId: Controller.Actions.YAW_LEFT -// label: "Yaw Left" -// min: -3.0 -// max: 3.0 -// size: 128 -// } -// -// ScrollingGraph { -// controlId: Controller.Actions.YAW_RIGHT -// label: "Yaw Right" -// min: -3.0 -// max: 3.0 -// size: 128 -// } -// } + + Row { + spacing: 8 + ScrollingGraph { + controlId: Controller.Actions.Yaw + label: "Yaw" + min: -3.0 + max: 3.0 + size: 128 + } + + ScrollingGraph { + controlId: Controller.Actions.YAW_LEFT + label: "Yaw Left" + min: -3.0 + max: 3.0 + size: 128 + } + + ScrollingGraph { + controlId: Controller.Actions.YAW_RIGHT + label: "Yaw Right" + min: -3.0 + max: 3.0 + size: 128 + } + } } } // dialog diff --git a/interface/resources/qml/controller/AnalogButton.qml b/interface/resources/qml/controller/AnalogButton.qml index d027332d42..82beb818ab 100644 --- a/interface/resources/qml/controller/AnalogButton.qml +++ b/interface/resources/qml/controller/AnalogButton.qml @@ -13,7 +13,7 @@ Item { property color color: 'black' function update() { - value = Controller.getValue(controlId); + value = controlId ? Controller.getValue(controlId) : 0; canvas.requestPaint(); } diff --git a/interface/resources/qml/controller/AnalogStick.qml b/interface/resources/qml/controller/AnalogStick.qml index 499e0e9ce9..c0d10bac59 100644 --- a/interface/resources/qml/controller/AnalogStick.qml +++ b/interface/resources/qml/controller/AnalogStick.qml @@ -17,8 +17,8 @@ Item { function update() { value = Qt.vector2d( - Controller.getValue(controlIds[0]), - Controller.getValue(controlIds[1]) + controlIds[0] ? Controller.getValue(controlIds[0]) : 0, + controlIds[1] ? Controller.getValue(controlIds[1]) : 0 ); if (root.invertY) { value.y = value.y * -1.0 @@ -27,7 +27,7 @@ Item { } Timer { - interval: 50; running: true; repeat: true + interval: 50; running: controlIds; repeat: true onTriggered: root.update() } diff --git a/interface/resources/qml/controller/ToggleButton.qml b/interface/resources/qml/controller/ToggleButton.qml index 6481045dd0..ee8bd380e2 100644 --- a/interface/resources/qml/controller/ToggleButton.qml +++ b/interface/resources/qml/controller/ToggleButton.qml @@ -12,12 +12,14 @@ Item { property real value: 0 property color color: 'black' + function update() { + value = controlId ? Controller.getValue(controlId) : 0; + canvas.requestPaint(); + } + Timer { - interval: 50; running: true; repeat: true - onTriggered: { - root.value = Controller.getValue(root.controlId); - canvas.requestPaint(); - } + interval: 50; running: root.controlId; repeat: true + onTriggered: root.update() } Canvas { diff --git a/interface/resources/qml/controller/Xbox.qml b/interface/resources/qml/controller/Xbox.qml index 4ff2959129..def2cf6fe8 100644 --- a/interface/resources/qml/controller/Xbox.qml +++ b/interface/resources/qml/controller/Xbox.qml @@ -100,5 +100,20 @@ Item { width: 16 * root.scale; height: 12 * root.scale x: (177 * root.scale); y: (45 * root.scale) } + + // Left primary + ToggleButton { + x: 0; y: parent.height - height; + controlId: root.device.LeftPrimaryThumb + width: 16 * root.scale; height: 16 * root.scale + } + + // Left primary + ToggleButton { + x: parent.width - width; y: parent.height - height; + controlId: root.device.RightPrimaryThumb + width: 16 * root.scale; height: 16 * root.scale + } + } } diff --git a/libraries/controllers/src/controllers/Conditional.cpp b/libraries/controllers/src/controllers/Conditional.cpp index 7173c80b76..00e42870e4 100644 --- a/libraries/controllers/src/controllers/Conditional.cpp +++ b/libraries/controllers/src/controllers/Conditional.cpp @@ -18,14 +18,4 @@ namespace controller { return Conditional::Pointer(); } - bool EndpointConditional::satisfied() { - if (!_endpoint) { - return false; - } - auto value = _endpoint->value(); - if (value == 0.0f) { - return false; - } - return true; - } } diff --git a/libraries/controllers/src/controllers/Conditional.h b/libraries/controllers/src/controllers/Conditional.h index d0226d5775..4d67d2871e 100644 --- a/libraries/controllers/src/controllers/Conditional.h +++ b/libraries/controllers/src/controllers/Conditional.h @@ -17,8 +17,6 @@ #include -#include "Endpoint.h" - class QJsonValue; namespace controller { @@ -41,14 +39,6 @@ namespace controller { static Factory _factory; }; - class EndpointConditional : public Conditional { - public: - EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) { } - virtual bool satisfied() override; - private: - Endpoint::Pointer _endpoint; - }; - } #define REGISTER_CONDITIONAL_CLASS(classEntry) \ diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 5d529ace30..7a94b06e7e 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -40,9 +40,12 @@ namespace controller { virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; virtual Pose pose() { return Pose(); } virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {} - virtual const bool isPose() { return _input.isPose(); } + virtual bool writeable() const { return true; } + virtual bool readable() const { return true; } + virtual void reset() { } + const Input& getInput() { return _input; } protected: @@ -61,6 +64,26 @@ namespace controller { ReadLambda _readLambda; WriteLambda _writeLambda; }; + + + class VirtualEndpoint : public Endpoint { + public: + VirtualEndpoint(const Input& id = Input::INVALID_INPUT) + : Endpoint(id) { + } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } + + virtual Pose pose() override { return _currentPose; } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { + _currentPose = newValue; + } + protected: + float _currentValue { 0.0f }; + Pose _currentPose {}; + }; + } #endif diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h index 98377b7434..6f997c9f91 100644 --- a/libraries/controllers/src/controllers/Input.h +++ b/libraries/controllers/src/controllers/Input.h @@ -55,6 +55,7 @@ struct Input { Input(const Input& src) : id(src.id) {} Input& operator = (const Input& src) { id = src.id; return (*this); } bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; } + bool operator !=(const Input& right) const { return !(*this == right); } bool operator < (const Input& src) const { return id < src.id; } static const Input INVALID_INPUT; diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index 9243ceb734..f0f9fbc012 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -30,6 +30,7 @@ namespace controller { Pose(const Pose&) = default; Pose& operator = (const Pose&) = default; bool operator ==(const Pose& right) const; + bool operator !=(const Pose& right) const { return !(*this == right); } bool isValid() const { return valid; } vec3 getTranslation() const { return translation; } quat getRotation() const { return rotation; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 6ef64fc784..ed688a47aa 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -55,22 +55,45 @@ private: float _lastValue = 0.0f; }; -class VirtualEndpoint : public Endpoint { +class StandardEndpoint : public VirtualEndpoint { public: - VirtualEndpoint(const Input& id = Input::INVALID_INPUT) - : Endpoint(id) { + StandardEndpoint(const Input& input) : VirtualEndpoint(input) {} + virtual bool writeable() const override { return !_written; } + virtual bool readable() const override { return !_read; } + virtual void reset() override { + apply(0.0f, 0.0f, Endpoint::Pointer()); + apply(Pose(), Pose(), Endpoint::Pointer()); + _written = _read = false; } - virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } + virtual float value() override { + _read = true; + return VirtualEndpoint::value(); + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) override { + // For standard endpoints, the first NON-ZERO write counts. + if (newValue != 0.0) { + _written = true; + } + VirtualEndpoint::apply(newValue, oldValue, source); + } + + virtual Pose pose() override { + _read = true; + return VirtualEndpoint::pose(); + } - virtual Pose pose() override { return _currentPose; } virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - _currentPose = newValue; + if (newValue != Pose()) { + _written = true; + } + VirtualEndpoint::apply(newValue, oldValue, source); } + private: - float _currentValue{ 0.0f }; - Pose _currentPose{}; + bool _written { false }; + bool _read { false }; }; @@ -136,12 +159,67 @@ public: virtual void apply(float newValue, float oldValue, const Pointer& source) { // Composites are read only } - -private: - Endpoint::Pointer _first; - Endpoint::Pointer _second; }; +class ArrayEndpoint : public Endpoint { + friend class UserInputMapper; +public: + using Pointer = std::shared_ptr; + ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { } + + virtual float value() override { + return 0.0; + } + + virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override { + for (auto& child : _children) { + if (child->writeable()) { + child->apply(newValue, oldValue, source); + } + } + } + + virtual bool readable() const override { return false; } + +private: + Endpoint::List _children; +}; + +class AnyEndpoint : public Endpoint { + friend class UserInputMapper; +public: + using Pointer = std::shared_ptr; + AnyEndpoint() : Endpoint(Input::INVALID_INPUT) {} + + virtual float value() override { + float result = 0; + for (auto& child : _children) { + float childResult = child->value(); + if (childResult != 0.0f) { + result = childResult; + } + } + return result; + } + + virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override { + qFatal("AnyEndpoint is read only"); + } + + virtual bool writeable() const override { return false; } + + virtual bool readable() const override { + for (auto& child : _children) { + if (!child->readable()) { + return false; + } + } + return true; + } + +private: + Endpoint::List _children; +}; class InputEndpoint : public Endpoint { public: @@ -150,40 +228,44 @@ public: } virtual float value() override { - _currentValue = 0.0f; + _read = true; if (isPose()) { - return _currentValue; + return pose().valid ? 1.0f : 0.0f; } auto userInputMapper = DependencyManager::get(); auto deviceProxy = userInputMapper->getDeviceProxy(_input); if (!deviceProxy) { - return _currentValue; + return 0.0f; } - _currentValue = deviceProxy->getValue(_input, 0); - return _currentValue; + return deviceProxy->getValue(_input, 0); } + + // FIXME need support for writing back to vibration / force feedback effects virtual void apply(float newValue, float oldValue, const Pointer& source) override {} virtual Pose pose() override { - _currentPose = Pose(); + _read = true; if (!isPose()) { - return _currentPose; + return Pose(); } auto userInputMapper = DependencyManager::get(); auto deviceProxy = userInputMapper->getDeviceProxy(_input); if (!deviceProxy) { - return _currentPose; + return Pose(); } - _currentPose = deviceProxy->getPose(_input, 0); - return _currentPose; + return deviceProxy->getPose(_input, 0); } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } + + virtual bool writeable() const { return !_written; } + virtual bool readable() const { return !_read; } + virtual void reset() { _written = _read = false; } private: - float _currentValue{ 0.0f }; - Pose _currentPose{}; + + bool _written { false }; + bool _read { false }; }; class ActionEndpoint : public Endpoint { @@ -194,9 +276,8 @@ public: virtual float value() override { return _currentValue; } virtual void apply(float newValue, float oldValue, const Pointer& source) override { - _currentValue += newValue; - if (!(_input == Input::INVALID_INPUT)) { + if (_input != Input::INVALID_INPUT) { auto userInputMapper = DependencyManager::get(); userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); } @@ -208,12 +289,17 @@ public: if (!_currentPose.isValid()) { return; } - if (!(_input == Input::INVALID_INPUT)) { + if (_input != Input::INVALID_INPUT) { auto userInputMapper = DependencyManager::get(); userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); } } + virtual void reset() override { + _currentValue = 0.0f; + _currentPose = Pose(); + } + private: float _currentValue{ 0.0f }; Pose _currentPose{}; @@ -254,7 +340,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { } Endpoint::Pointer endpoint; if (input.device == STANDARD_DEVICE) { - endpoint = std::make_shared(input); + endpoint = std::make_shared(input); } else if (input.device == ACTIONS_DEVICE) { endpoint = std::make_shared(input); } else { @@ -396,20 +482,7 @@ void UserInputMapper::update(float deltaTime) { } // Run the mappings code - update(); - - // Scale all the channel step with the scale - for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { - if (_externalActionStates[i] != 0) { - _actionStates[i] += _externalActionStates[i]; - _externalActionStates[i] = 0.0f; - } - - if (_externalPoseStates[i].isValid()) { - _poseStates[i] = _externalPoseStates[i]; - _externalPoseStates[i] = Pose(); - } - } + runMappings(); // merge the bisected and non-bisected axes for now fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]); @@ -419,7 +492,6 @@ void UserInputMapper::update(float deltaTime) { fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]); fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]); - static const float EPSILON = 0.01f; for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { _actionStates[i] *= _actionScales[i]; @@ -561,53 +633,62 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { return Input(STANDARD_DEVICE, pose, ChannelType::POSE); } -void UserInputMapper::update() { +void UserInputMapper::runMappings() { static auto deviceNames = getDeviceNames(); - _overrideValues.clear(); + _overrides.clear(); - EndpointSet readEndpoints; - EndpointSet writtenEndpoints; + for (auto endpointEntry : this->_endpointsByInput) { + endpointEntry.second->reset(); + } // Now process the current values for each level of the stack for (auto& mapping : _activeMappings) { for (const auto& route : mapping->routes) { - const auto& source = route->source; - // 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 - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when - // I press the button. The exception is if I'm wiring a control back to itself - // in order to adjust my interface, like inverting the Y axis on an analog stick - if (readEndpoints.count(source)) { - continue; - } - - const auto& destination = route->destination; - // THis could happen if the route destination failed to create - // FIXME: Maybe do not create the route if the destination failed and avoid this case ? - if (!destination) { - continue; - } - - if (writtenEndpoints.count(destination)) { - continue; - } - if (route->conditional) { if (!route->conditional->satisfied()) { continue; } } - // Standard controller destinations can only be can only be used once. - if (getStandardDeviceID() == destination->getInput().getDevice()) { - writtenEndpoints.insert(destination); + auto source = route->source; + if (_overrides.count(source)) { + source = _overrides[source]; + } + + // 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 + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // I press the button. The exception is if I'm wiring a control back to itself + // in order to adjust my interface, like inverting the Y axis on an analog stick + if (!source->readable()) { + continue; + } + + + auto input = source->getInput(); + float value = source->value(); + if (value != 0.0) { + int i = 0; + } + + auto destination = route->destination; + // THis could happen if the route destination failed to create + // FIXME: Maybe do not create the route if the destination failed and avoid this case ? + if (!destination) { + continue; + } + + // FIXME?, should come before or after the override logic? + if (!destination->writeable()) { + continue; } // Only consume the input if the route isn't a loopback. // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` - bool loopback = source == destination; - if (!loopback) { - readEndpoints.insert(source); + bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT); + // Each time we loop back we re-write the override + if (loopback) { + _overrides[source] = destination = std::make_shared(source->getInput()); } // Fetch the value, may have been overriden by previous loopback routes @@ -624,14 +705,11 @@ void UserInputMapper::update() { value = filter->apply(value); } - if (loopback) { - _overrideValues[source] = value; - } else { - destination->apply(value, 0, source); - } + destination->apply(value, 0, source); } } } + } Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { @@ -741,9 +819,9 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { } float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { - auto valuesIterator = _overrideValues.find(endpoint); - if (_overrideValues.end() != valuesIterator) { - return valuesIterator->second; + auto valuesIterator = _overrides.find(endpoint); + if (_overrides.end() != valuesIterator) { + return valuesIterator->second->value(); } return endpoint->value(); @@ -787,27 +865,70 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { return parseMapping(json); } - -const QString JSON_NAME = QStringLiteral("name"); -const QString JSON_CHANNELS = QStringLiteral("channels"); -const QString JSON_CHANNEL_FROM = QStringLiteral("from"); -const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); -const QString JSON_CHANNEL_TO = QStringLiteral("to"); -const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); +static const QString JSON_NAME = QStringLiteral("name"); +static const QString JSON_CHANNELS = QStringLiteral("channels"); +static const QString JSON_CHANNEL_FROM = QStringLiteral("from"); +static const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); +static const QString JSON_CHANNEL_TO = QStringLiteral("to"); +static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { + Endpoint::Pointer result; if (value.isString()) { auto input = findDeviceInput(value.toString()); - return endpointFor(input); + result = endpointFor(input); } else if (value.isObject()) { // Endpoint is defined as an object, we expect a js function then return Endpoint::Pointer(); } - return Endpoint::Pointer(); + + if (!result) { + qWarning() << "Invalid endpoint definition " << value; + } + return result; } +class AndConditional : public Conditional { +public: + using Pointer = std::shared_ptr; + + AndConditional(Conditional::List children) : _children(children) { } + + virtual bool satisfied() override { + for (auto& conditional : _children) { + if (!conditional->satisfied()) { + return false; + } + } + return true; + } + +private: + Conditional::List _children; +}; + +class EndpointConditional : public Conditional { +public: + EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} + virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; } +private: + Endpoint::Pointer _endpoint; +}; + Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { - if (value.isString()) { + if (value.isArray()) { + // Support "when" : [ "GamePad.RB", "GamePad.LB" ] + Conditional::List children; + for (auto arrayItem : value.toArray()) { + Conditional::Pointer childConditional = parseConditional(arrayItem); + if (!childConditional) { + return Conditional::Pointer(); + } + children.push_back(childConditional); + } + return std::make_shared(children); + } else if (value.isString()) { + // Support "when" : "GamePad.RB" auto input = findDeviceInput(value.toString()); auto endpoint = endpointFor(input); if (!endpoint) { @@ -815,11 +936,85 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) } return std::make_shared(endpoint); - } - + } + return Conditional::parse(value); } + +Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { + Filter::Pointer result; + if (value.isString()) { + result = Filter::getFactory().create(value.toString()); + } else if (value.isObject()) { + result = Filter::parse(value.toObject()); + } + + if (!result) { + qWarning() << "Invalid filter definition " << value; + } + + return result; +} + + +Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { + if (value.isNull()) { + return Filter::List(); + } + + if (value.isArray()) { + Filter::List result; + auto filtersArray = value.toArray(); + for (auto filterValue : filtersArray) { + Filter::Pointer filter = parseFilter(filterValue); + if (!filter) { + return Filter::List(); + } + result.push_back(filter); + } + return result; + } + + Filter::Pointer filter = parseFilter(value); + if (!filter) { + return Filter::List(); + } + return Filter::List({ filter }); +} + +Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { + if (value.isArray()) { + ArrayEndpoint::Pointer result = std::make_shared(); + for (auto arrayItem : value.toArray()) { + Endpoint::Pointer destination = parseEndpoint(arrayItem); + if (!destination) { + return Endpoint::Pointer(); + } + result->_children.push_back(destination); + } + return result; + } + + return parseEndpoint(value); +} + +Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) { + if (value.isArray()) { + AnyEndpoint::Pointer result = std::make_shared(); + for (auto arrayItem : value.toArray()) { + Endpoint::Pointer destination = parseEndpoint(arrayItem); + if (!destination) { + return Endpoint::Pointer(); + } + result->_children.push_back(destination); + } + return result; + } + + return parseEndpoint(value); +} + Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { if (!value.isObject()) { return Route::Pointer(); @@ -827,47 +1022,37 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { const auto& obj = value.toObject(); Route::Pointer result = std::make_shared(); - result->source = parseEndpoint(obj[JSON_CHANNEL_FROM]); + result->source = parseSource(obj[JSON_CHANNEL_FROM]); if (!result->source) { qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; return Route::Pointer(); } - result->destination = parseEndpoint(obj[JSON_CHANNEL_TO]); + + + result->destination = parseDestination(obj[JSON_CHANNEL_TO]); if (!result->destination) { qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO]; return Route::Pointer(); } if (obj.contains(JSON_CHANNEL_WHEN)) { - auto when = parseConditional(obj[JSON_CHANNEL_WHEN]); - if (!when) { - qWarning() << "Invalid route conditional " << obj[JSON_CHANNEL_TO]; + auto conditionalsValue = obj[JSON_CHANNEL_WHEN]; + result->conditional = parseConditional(conditionalsValue); + if (!result->conditional) { + qWarning() << "Invalid route conditionals " << conditionalsValue; return Route::Pointer(); } - result->conditional = when; } - const auto& filtersValue = obj[JSON_CHANNEL_FILTERS]; - // FIXME support strings for filters with no parameters, both in the array and at the top level... - // i.e. - // { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : "invert" }, - // and - // { "from": "Standard.DU", "to" : "Actions.LONGITUDINAL_FORWARD", "filters" : [ "invert", "constrainToInteger" ] }, - if (filtersValue.isArray()) { - auto filtersArray = filtersValue.toArray(); - for (auto filterValue : filtersArray) { - if (!filterValue.isObject()) { - qWarning() << "Invalid filter " << filterValue; - return Route::Pointer(); - } - Filter::Pointer filter = Filter::parse(filterValue.toObject()); - if (!filter) { - qWarning() << "Invalid filter " << filterValue; - return Route::Pointer(); - } - result->filters.push_back(filter); + if (obj.contains(JSON_CHANNEL_FILTERS)) { + auto filtersValue = obj[JSON_CHANNEL_FILTERS]; + result->filters = parseFilters(filtersValue); + if (result->filters.empty()) { + qWarning() << "Invalid route filters " << filtersValue; + return Route::Pointer(); } } + return result; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index ec1267cd0c..345bba8c2b 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -50,7 +50,7 @@ namespace controller { using MappingStack = std::list; using InputToEndpointMap = std::map; using EndpointSet = std::unordered_set; - using ValueMap = std::map; + using EndpointOverrideMap = std::map; using EndpointPair = std::pair; using EndpointPairMap = std::map; using DevicesMap = std::map; @@ -86,9 +86,9 @@ namespace controller { int findAction(const QString& actionName) const; QVector getActionNames() const; - void setActionState(Action action, float value) { _externalActionStates[toInt(action)] = value; } - void deltaActionState(Action action, float delta) { _externalActionStates[toInt(action)] += delta; } - void setActionState(Action action, const Pose& value) { _externalPoseStates[toInt(action)] = value; } + void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; } + void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; } + void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } static Input makeStandardInput(controller::StandardButtonChannel button); static Input makeStandardInput(controller::StandardAxisChannel axis); @@ -119,7 +119,7 @@ namespace controller { void hardwareChanged(); protected: - virtual void update(); + virtual void runMappings(); // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } @@ -128,11 +128,9 @@ namespace controller { uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; std::vector _actionStates = std::vector(toInt(Action::NUM_ACTIONS), 0.0f); - std::vector _externalActionStates = std::vector(toInt(Action::NUM_ACTIONS), 0.0f); 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 _externalPoseStates = std::vector(toInt(Action::NUM_ACTIONS)); glm::mat4 _sensorToWorldMat; @@ -148,16 +146,22 @@ namespace controller { Endpoint::Pointer endpointFor(const QScriptValue& endpoint); Endpoint::Pointer endpointFor(const Input& endpoint) const; Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); + Mapping::Pointer parseMapping(const QJsonValue& json); Route::Pointer parseRoute(const QJsonValue& value); - Conditional::Pointer parseConditional(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); + + static Filter::Pointer parseFilter(const QJsonValue& value); + static Filter::List parseFilters(const QJsonValue& value); InputToEndpointMap _endpointsByInput; EndpointToInputMap _inputsByEndpoint; EndpointPairMap _compositeEndpoints; - ValueMap _overrideValues; + EndpointOverrideMap _overrides; MappingNameMap _mappingsByName; Mapping::Pointer _defaultMapping{ std::make_shared("Default") }; MappingDeviceMap _mappingsByDevice; From 4a1df286fda6eb0805d52c71cc61518a6d27df7b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 15:57:37 -0700 Subject: [PATCH 086/232] Cleanup of enums and JS names --- interface/resources/qml/TestControllers.qml | 27 +---- .../resources/qml/controller/Standard.qml | 35 ++++++ interface/resources/qml/controller/Xbox.qml | 15 --- interface/src/devices/3DConnexionClient.cpp | 50 ++++----- interface/src/devices/3DConnexionClient.h | 18 ++-- .../controllers/src/controllers/Actions.cpp | 102 +++++++++++++----- .../src/controllers/StandardController.cpp | 8 +- .../src/controllers/StandardControls.h | 8 +- .../input-plugins/ViveControllerManager.cpp | 4 +- 9 files changed, 150 insertions(+), 117 deletions(-) create mode 100644 interface/resources/qml/controller/Standard.qml diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index 71a836f2e4..a5deaed159 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -131,27 +131,8 @@ HifiControls.VrDialog { onClicked: { var mapping = Controller.newMapping(); // Inverting a value - mapping.from(hydra.RY).invert().to(standard.RY); - mapping.from(hydra.RX).to(standard.RX); - mapping.from(hydra.LY).to(standard.LY); - mapping.from(hydra.LX).to(standard.LX); - // Assigning a value from a function - // mapping.from(function() { return Math.sin(Date.now() / 250); }).to(standard.RX); - // Constrainting a value to -1, 0, or 1, with a deadzone -// mapping.from(xbox.LY).deadZone(0.5).constrainToInteger().to(standard.LY); + mapping.from(standard.RY).invert().to(standard.RY); mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); -// mapping.from(actions.Yaw).clamp(0, 1).invert().to(actions.YAW_RIGHT); -// mapping.from(actions.Yaw).clamp(-1, 0).to(actions.YAW_LEFT); - // mapping.modifier(keyboard.Ctrl).scale(2.0) -// mapping.from(keyboard.A).to(actions.TranslateLeft) -// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.A, keyboard.Shift, keyboard.Ctrl).scale(2.0).to(actions.TurnLeft) -// // First loopbacks -// // Then non-loopbacks by constraint level (number of inputs) -// mapping.from(xbox.RX).deadZone(0.2).to(xbox.RX) -// mapping.from(standard.RB, standard.LB, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.A, keyboard.Shift).to(actions.TurnLeft) -// mapping.from(keyboard.W).when(keyboard.Shift).to(actions.Forward) testMapping = mapping; enabled = false text = "Built" @@ -175,13 +156,13 @@ HifiControls.VrDialog { } Row { - Xbox { device: root.standard; label: "Standard"; width: 360 } + Standard { device: root.standard; label: "Standard"; width: 180 } } Row { spacing: 8 - Xbox { device: root.xbox; label: "XBox"; width: 360 } - Hydra { device: root.hydra; width: 360 } + Xbox { device: root.xbox; label: "XBox"; width: 180 } + Hydra { device: root.hydra; width: 180 } } Row { diff --git a/interface/resources/qml/controller/Standard.qml b/interface/resources/qml/controller/Standard.qml new file mode 100644 index 0000000000..cee79fe50c --- /dev/null +++ b/interface/resources/qml/controller/Standard.qml @@ -0,0 +1,35 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Dialogs 1.0 + +import "xbox" + +Item { + id: root + property real aspect: 300.0 / 215.0 + width: 300 + height: width / aspect + property var device + property string label: "" + property real scale: width / 300.0 + + Xbox { + width: root.width; height: root.height + device: root.device + } + + // Left primary + ToggleButton { + x: 0; y: parent.height - height; + controlId: root.device.LeftPrimaryThumb + width: 16 * root.scale; height: 16 * root.scale + } + + // Left primary + ToggleButton { + x: parent.width - width; y: parent.height - height; + controlId: root.device.RB + width: 16 * root.scale; height: 16 * root.scale + } +} diff --git a/interface/resources/qml/controller/Xbox.qml b/interface/resources/qml/controller/Xbox.qml index def2cf6fe8..4ff2959129 100644 --- a/interface/resources/qml/controller/Xbox.qml +++ b/interface/resources/qml/controller/Xbox.qml @@ -100,20 +100,5 @@ Item { width: 16 * root.scale; height: 12 * root.scale x: (177 * root.scale); y: (45 * root.scale) } - - // Left primary - ToggleButton { - x: 0; y: parent.height - height; - controlId: root.device.LeftPrimaryThumb - width: 16 * root.scale; height: 16 * root.scale - } - - // Left primary - ToggleButton { - x: parent.width - width; y: parent.height - height; - controlId: root.device.RightPrimaryThumb - width: 16 * root.scale; height: 16 * root.scale - } - } } diff --git a/interface/src/devices/3DConnexionClient.cpp b/interface/src/devices/3DConnexionClient.cpp index 05795e87e9..7c44e4eed7 100755 --- a/interface/src/devices/3DConnexionClient.cpp +++ b/interface/src/devices/3DConnexionClient.cpp @@ -33,18 +33,14 @@ ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {} void ConnexionData::handleAxisEvent() { - _axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(ROTATION_AXIS_X_POS).getChannel()] = (cc_rotation.x > 0.0f) ? cc_rotation.x / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(ROTATION_AXIS_X_NEG).getChannel()] = (cc_rotation.x < 0.0f) ? -cc_rotation.x / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(ROTATION_AXIS_Z_POS).getChannel()] = (cc_rotation.z > 0.0f) ? cc_rotation.z / MAX_AXIS : 0.0f; - _axisStateMap[makeInput(ROTATION_AXIS_Z_NEG).getChannel()] = (cc_rotation.z < 0.0f) ? -cc_rotation.z / MAX_AXIS : 0.0f; + auto rotation = cc_rotation / MAX_AXIS; + _axisStateMap[ROTATE_X] = rotation.x; + _axisStateMap[ROTATE_Y] = rotation.y; + _axisStateMap[ROTATE_Z] = rotation.z; + auto position = cc_rotation / MAX_AXIS; + _axisStateMap[TRANSLATE_X] = position.x; + _axisStateMap[TRANSLATE_Y] = position.y; + _axisStateMap[TRANSLATE_Z] = position.z; } void ConnexionData::setButton(int lastButtonState) { @@ -57,24 +53,18 @@ void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs; - - availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_1), "Left button")); - availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_2), "Right button")); - availableInputs.append(controller::Input::NamedPair(makeInput(BUTTON_3), "Both buttons")); - - availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward")); - availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Y_POS), "Move forward")); - availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_X_POS), "Move right")); - availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_X_NEG), "Move Left")); - availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Z_POS), "Move up")); - availableInputs.append(controller::Input::NamedPair(makeInput(POSITION_AXIS_Z_NEG), "Move down")); - availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward")); - availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward")); - availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right")); - availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left")); - availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up")); - availableInputs.append(controller::Input::NamedPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down")); + using namespace controller; + static QVector availableInputs { + Input::NamedPair(makeInput(BUTTON_1), "LeftButton"), + Input::NamedPair(makeInput(BUTTON_2), "RightButton"), + Input::NamedPair(makeInput(BUTTON_3), "BothButtons"), + Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"), + Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"), + Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"), + Input::NamedPair(makeInput(ROTATE_X), "RotateX"), + Input::NamedPair(makeInput(ROTATE_Y), "RotateY"), + Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"), + }; return availableInputs; }; } diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index 8f66a602a4..8489d54913 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -182,18 +182,12 @@ public: static ConnexionData& getInstance(); ConnexionData(); enum PositionChannel { - POSITION_AXIS_X_POS = 1, - POSITION_AXIS_X_NEG = 2, - POSITION_AXIS_Y_POS = 3, - POSITION_AXIS_Y_NEG = 4, - POSITION_AXIS_Z_POS = 5, - POSITION_AXIS_Z_NEG = 6, - ROTATION_AXIS_X_POS = 7, - ROTATION_AXIS_X_NEG = 8, - ROTATION_AXIS_Y_POS = 9, - ROTATION_AXIS_Y_NEG = 10, - ROTATION_AXIS_Z_POS = 11, - ROTATION_AXIS_Z_NEG = 12 + TRANSLATE_X, + TRANSLATE_Y, + TRANSLATE_Z, + ROTATE_X, + ROTATE_Y, + ROTATE_Z, }; enum ButtonChannel { diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 78ba42db8b..7954ab5522 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -12,6 +12,23 @@ namespace controller { + Input::NamedPair makePair(ChannelType type, Action action, const QString& name) { + auto input = Input(UserInputMapper::ACTIONS_DEVICE, toInt(action), type); + return Input::NamedPair(input, name); + } + + Input::NamedPair makeAxisPair(Action action, const QString& name) { + return makePair(ChannelType::AXIS, action, name); + } + + Input::NamedPair makeButtonPair(Action action, const QString& name) { + return makePair(ChannelType::BUTTON, action, name); + } + + Input::NamedPair makePosePair(Action action, const QString& name) { + return makePair(ChannelType::POSE, action, name); + } + // Device functions void ActionsDevice::buildDeviceProxy(DeviceProxy::Pointer proxy) { proxy->_name = _name; @@ -19,33 +36,64 @@ namespace controller { proxy->getAxis = [this](const Input& input, int timestamp) -> float { return 0; }; proxy->getAvailabeInputs = [this]() -> QVector { QVector availableInputs{ - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LONGITUDINAL_BACKWARD), ChannelType::AXIS), "LONGITUDINAL_BACKWARD"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LONGITUDINAL_FORWARD), ChannelType::AXIS), "LONGITUDINAL_FORWARD"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LATERAL_LEFT), ChannelType::AXIS), "LATERAL_LEFT"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LATERAL_RIGHT), ChannelType::AXIS), "LATERAL_RIGHT"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::VERTICAL_DOWN), ChannelType::AXIS), "VERTICAL_DOWN"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::VERTICAL_UP), ChannelType::AXIS), "VERTICAL_UP"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::YAW_LEFT), ChannelType::AXIS), "YAW_LEFT"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::YAW_RIGHT), ChannelType::AXIS), "YAW_RIGHT"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::PITCH_DOWN), ChannelType::AXIS), "PITCH_DOWN"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::PITCH_UP), ChannelType::AXIS), "PITCH_UP"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::BOOM_IN), ChannelType::AXIS), "BOOM_IN"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::BOOM_OUT), ChannelType::AXIS), "BOOM_OUT"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LEFT_HAND), ChannelType::POSE), "LEFT_HAND"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::RIGHT_HAND), ChannelType::POSE), "RIGHT_HAND"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::LEFT_HAND_CLICK), ChannelType::BUTTON), "LEFT_HAND_CLICK"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::RIGHT_HAND_CLICK), ChannelType::BUTTON), "RIGHT_HAND_CLICK"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::SHIFT), ChannelType::BUTTON), "SHIFT"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::ACTION1), ChannelType::BUTTON), "ACTION1"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::ACTION2), ChannelType::BUTTON), "ACTION2"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::CONTEXT_MENU), ChannelType::BUTTON), "CONTEXT_MENU"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TOGGLE_MUTE), ChannelType::AXIS), "TOGGLE_MUTE"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TRANSLATE_X), ChannelType::AXIS), "TranslateX"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TRANSLATE_Y), ChannelType::AXIS), "TranslateY"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::TRANSLATE_Z), ChannelType::AXIS), "TranslateZ"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::ROLL), ChannelType::AXIS), "Roll"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::PITCH), ChannelType::AXIS), "Pitch"), - Input::NamedPair(Input(UserInputMapper::ACTIONS_DEVICE, toInt(Action::YAW), ChannelType::AXIS), "Yaw") + makeAxisPair(Action::TRANSLATE_X, "TranslateX"), + makeAxisPair(Action::TRANSLATE_Y, "TranslateY"), + makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"), + makeAxisPair(Action::ROLL, "Roll"), + makeAxisPair(Action::PITCH, "Pitch"), + makeAxisPair(Action::YAW, "Yaw"), + makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), + makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), + makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), + makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"), + makeAxisPair(Action::VERTICAL_DOWN, "Down"), + makeAxisPair(Action::VERTICAL_UP, "Up"), + makeAxisPair(Action::YAW_LEFT, "YawLeft"), + makeAxisPair(Action::YAW_RIGHT, "YawRight"), + makeAxisPair(Action::PITCH_DOWN, "PitchDown"), + makeAxisPair(Action::PITCH_UP, "PitchUp"), + makeAxisPair(Action::BOOM_IN, "BoomIn"), + makeAxisPair(Action::BOOM_OUT, "BoomOut"), + + makePosePair(Action::LEFT_HAND, "LeftHand"), + makePosePair(Action::RIGHT_HAND, "RightHand"), + + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), + makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), + + makeButtonPair(Action::SHIFT, "Shift"), + makeButtonPair(Action::ACTION1, "PrimaryAction"), + makeButtonPair(Action::ACTION2, "SecondaryAction"), + makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), + makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), + + + // Deprecated aliases + // FIXME remove after we port all scripts + makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), + makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"), + makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"), + makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"), + makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"), + makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"), + makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"), + makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"), + makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"), + makeAxisPair(Action::PITCH_UP, "PITCH_UP"), + makeAxisPair(Action::BOOM_IN, "BOOM_IN"), + makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"), + + makePosePair(Action::LEFT_HAND, "LEFT_HAND"), + makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"), + + makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"), + makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"), + + makeButtonPair(Action::SHIFT, "SHIFT"), + makeButtonPair(Action::ACTION1, "ACTION1"), + makeButtonPair(Action::ACTION2, "ACTION2"), + makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"), + makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"), }; return availableInputs; }; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 061fc4ea56..5734174284 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -95,10 +95,10 @@ void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) { availableInputs.append(makePair(DR, "Right")); - availableInputs.append(makePair(LeftPrimaryThumb, "LeftPrimaryThumb")); - availableInputs.append(makePair(LeftSecondaryThumb, "LeftSecondaryThumb")); - availableInputs.append(makePair(RightPrimaryThumb, "RightPrimaryThumb")); - availableInputs.append(makePair(RightSecondaryThumb, "RightSecondaryThumb")); + availableInputs.append(makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb")); + availableInputs.append(makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb")); + availableInputs.append(makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb")); + availableInputs.append(makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb")); return availableInputs; }; diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index 26644e2f38..b051f68c13 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -37,10 +37,10 @@ namespace controller { DR, // These don't map to SDL types - LeftPrimaryThumb, - LeftSecondaryThumb, - RightPrimaryThumb, - RightSecondaryThumb, + LEFT_PRIMARY_THUMB, + LEFT_SECONDARY_THUMB, + RIGHT_PRIMARY_THUMB, + RIGHT_SECONDARY_THUMB, NUM_STANDARD_BUTTONS }; diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 66697e8b11..21b7b81173 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -414,10 +414,10 @@ void ViveControllerManager::buildDeviceProxy(controller::DeviceProxy::Pointer pr makePair(LS, "LS"), makePair(RS, "RS"), + makePair(LEFT_HAND, "LeftHand"), + makePair(RIGHT_HAND, "RightHand"), }; - //availableInputs.append(Input::NamedPair(makeInput(LEFT_HAND), "Left Hand")); - //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); From 9b11b2091f4aa6d33202e29829048cdd7be9cd44 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 16:17:11 -0700 Subject: [PATCH 087/232] fix hydra left right arm flip --- .../input-plugins/src/input-plugins/SixenseManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index c9b8ba11ce..3f612a39f3 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -230,7 +230,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { if (!jointsCaptured) { // Rotation of Palm glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); - handlePoseEvent(position, rotation, numActiveControllers - 1); + handlePoseEvent(position, rotation, left); } else { _poseStateMap.clear(); } @@ -384,7 +384,12 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { } } +#include + void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left) { + + qDebug() << "SixenseManager::handlePoseEvent() position:" << position << "rotation:" << rotation << "left:" << left; + #ifdef HAVE_SIXENSE // From ABOVE the sixense coordinate frame looks like this: // From 63e6452630b42836ada2c8d6436102f1bafaef2f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 16:17:58 -0700 Subject: [PATCH 088/232] fix hydra left right arm flip --- libraries/input-plugins/src/input-plugins/SixenseManager.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3f612a39f3..3dc983cb34 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -384,12 +384,7 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { } } -#include - void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left) { - - qDebug() << "SixenseManager::handlePoseEvent() position:" << position << "rotation:" << rotation << "left:" << left; - #ifdef HAVE_SIXENSE // From ABOVE the sixense coordinate frame looks like this: // From a3900a954b7e9c1db9a17a0a867a24b73ecf573c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 17:03:54 -0700 Subject: [PATCH 089/232] expose MyAvatar.leftHandePosition and MyAvatar.rightHandPosition to JS --- interface/src/avatar/MyAvatar.cpp | 36 +++++++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 11 +++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5920543dca..7d0f9edaa0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -524,6 +524,42 @@ void MyAvatar::updateFromTrackers(float deltaTime) { } +// FIXME - this is super duper dumb... but this is how master works. When you have +// hydras plugged in, you'll get 4 "palms" but only the number of controllers lifted +// of the base station are considered active. So when you ask for "left" you get the +// first active controller. If you have both controllers held up or just the left, that +// will be correct. But if you lift the right controller, then it will be reported +// as "left"... you also see this in the avatars hands. +const PalmData* MyAvatar::getActivePalm(int palmIndex) const { + const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); + int numberOfPalms = handData->getNumPalms(); + int numberOfActivePalms = 0; + for (int i = 0; i < numberOfPalms; i++) { + auto palm = handData->getPalms()[i]; + if (palm.isActive()) { + // if we've reached the requested "active" palm, then we will return it + if (numberOfActivePalms == palmIndex) { + return &handData->getPalms()[i]; + } + numberOfActivePalms++; + } + } + return NULL; +} + + +glm::vec3 MyAvatar::getLeftHandPosition() const { + const int LEFT_HAND = 0; + auto palmData = getActivePalm(LEFT_HAND); + return palmData ? palmData->getPosition() : glm::vec3(0.0f); +} + +glm::vec3 MyAvatar::getRightHandPosition() const { + const int RIGHT_HAND = 1; + auto palmData = getActivePalm(RIGHT_HAND); + return palmData ? palmData->getPosition() : glm::vec3(0.0f); +} + // virtual void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { // don't render if we've been asked to disable local rendering diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7347419fee..d9ac2db271 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -34,7 +34,6 @@ enum AudioListenerMode { }; Q_DECLARE_METATYPE(AudioListenerMode); - class MyAvatar : public Avatar { Q_OBJECT Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) @@ -50,6 +49,10 @@ class MyAvatar : public Avatar { Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) + + Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) + Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition) + public: MyAvatar(RigPointer rig); ~MyAvatar(); @@ -136,6 +139,9 @@ public: Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } + Q_INVOKABLE glm::vec3 getLeftHandPosition() const; + Q_INVOKABLE glm::vec3 getRightHandPosition() const; + AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); @@ -274,6 +280,9 @@ private: void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); + const PalmData* getActivePalm(int palmIndex) const; + + // derive avatar body position and orientation from the current HMD Sensor location. // results are in sensor space glm::mat4 deriveBodyFromHMDSensor() const; From 68a2985b7abedd08d49c1bfb73abfeae4dca1c83 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 17:14:48 -0700 Subject: [PATCH 090/232] add tip position as well --- interface/src/avatar/MyAvatar.cpp | 12 ++++++++++++ interface/src/avatar/MyAvatar.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7d0f9edaa0..5202138147 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -560,6 +560,18 @@ glm::vec3 MyAvatar::getRightHandPosition() const { return palmData ? palmData->getPosition() : glm::vec3(0.0f); } +glm::vec3 MyAvatar::getLeftHandTipPosition() const { + const int LEFT_HAND = 0; + auto palmData = getActivePalm(LEFT_HAND); + return palmData ? palmData->getTipPosition() : glm::vec3(0.0f); +} + +glm::vec3 MyAvatar::getRightHandTipPosition() const { + const int RIGHT_HAND = 1; + auto palmData = getActivePalm(RIGHT_HAND); + return palmData ? palmData->getTipPosition() : glm::vec3(0.0f); +} + // virtual void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { // don't render if we've been asked to disable local rendering diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d9ac2db271..4a0ae514f6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -52,6 +52,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition) + Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition) + Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition) public: MyAvatar(RigPointer rig); @@ -141,6 +143,8 @@ public: Q_INVOKABLE glm::vec3 getLeftHandPosition() const; Q_INVOKABLE glm::vec3 getRightHandPosition() const; + Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const; + Q_INVOKABLE glm::vec3 getRightHandTipPosition() const; AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); From 637654adeacce63cdd0da4495ec5b609818c7a8f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 18:40:13 -0700 Subject: [PATCH 091/232] Wiring up step yaw --- examples/tests/controllerInterfaceTest.js | 11 + interface/resources/qml/ScrollingGraph.qml | 12 +- interface/resources/qml/TestControllers.qml | 147 +++--------- .../resources/qml/controller/Standard.qml | 2 +- .../controllers/src/controllers/Actions.cpp | 7 +- .../controllers/src/controllers/Actions.h | 10 + .../src/controllers/UserInputMapper.cpp | 211 +++++++++++------- .../src/controllers/UserInputMapper.h | 17 +- 8 files changed, 210 insertions(+), 207 deletions(-) diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index fa8cf48b9b..48ad8f0879 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -1,4 +1,12 @@ ControllerTest = function() { + var standard = Controller.Standard; + var actions = Controller.Actions; + this.mappingEnabled = false; + this.mapping = Controller.newMapping(); + this.mapping.from(standard.RX).to(actions.StepYaw); + this.mapping.enable(); + this.mappingEnabled = true; + print("Actions"); for (var prop in Controller.Actions) { @@ -24,6 +32,9 @@ ControllerTest = function() { } ControllerTest.prototype.onCleanup = function() { + if (this.mappingEnabled) { + this.mapping.disable(); + } } diff --git a/interface/resources/qml/ScrollingGraph.qml b/interface/resources/qml/ScrollingGraph.qml index b5eaac6f89..26ca9a61ff 100644 --- a/interface/resources/qml/ScrollingGraph.qml +++ b/interface/resources/qml/ScrollingGraph.qml @@ -3,12 +3,12 @@ import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 import QtQuick.Dialogs 1.0 -Item { +Rectangle { id: root property int size: 64 width: size height: size - + color: 'black' property int controlId: 0 property real value: 0.5 property int scrollWidth: 1 @@ -16,7 +16,7 @@ Item { property real max: 1.0 property bool log: false property real range: max - min - property color color: 'blue' + property color lineColor: 'yellow' property bool bar: false property real lastHeight: -1 property string label: "" @@ -49,19 +49,21 @@ Item { Text { anchors.top: parent.top text: root.label - + color: 'white' } Text { anchors.right: parent.right anchors.top: parent.top text: root.max + color: 'white' } Text { anchors.right: parent.right anchors.bottom: parent.bottom text: root.min + color: 'white' } function scroll() { @@ -92,7 +94,7 @@ Item { ctx.beginPath(); ctx.lineWidth = 1 - ctx.strokeStyle = root.color + ctx.strokeStyle = root.lineColor ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) ctx.stroke() ctx.restore() diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index a5deaed159..f1b8640c02 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -20,8 +20,20 @@ HifiControls.VrDialog { property var standard: Controller.Standard property var hydra: null property var testMapping: null + property bool testMappingEnabled: false property var xbox: null + function buildMapping() { + testMapping = Controller.newMapping(); + testMapping.from(standard.RY).invert().to(actions.Pitch); + testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); + testMapping.from(standard.RX).to(actions.StepYaw); + } + + function toggleMapping() { + testMapping.enable(!testMappingEnabled); + testMappingEnabled = !testMappingEnabled; + } Component.onCompleted: { enabled = true @@ -49,110 +61,18 @@ HifiControls.VrDialog { Row { spacing: 8 - Button { - text: "Standard Mapping" - onClicked: { - var mapping = Controller.newMapping("Default"); - mapping.from(standard.LX).to(actions.TranslateX); - mapping.from(standard.LY).to(actions.TranslateZ); - mapping.from(standard.RY).to(actions.Pitch); - mapping.from(standard.RX).to(actions.Yaw); - mapping.from(standard.DU).scale(0.5).to(actions.LONGITUDINAL_FORWARD); - mapping.from(standard.DD).scale(0.5).to(actions.LONGITUDINAL_BACKWARD); - mapping.from(standard.DL).scale(0.5).to(actions.LATERAL_LEFT); - mapping.from(standard.DR).scale(0.5).to(actions.LATERAL_RIGHT); - mapping.from(standard.X).to(actions.VERTICAL_DOWN); - mapping.from(standard.Y).to(actions.VERTICAL_UP); - mapping.from(standard.RT).scale(0.1).to(actions.BOOM_IN); - mapping.from(standard.LT).scale(0.1).to(actions.BOOM_OUT); - mapping.from(standard.B).to(actions.ACTION1); - mapping.from(standard.A).to(actions.ACTION2); - mapping.from(standard.RB).to(actions.SHIFT); - mapping.from(standard.Back).to(actions.TOGGLE_MUTE); - mapping.from(standard.Start).to(actions.CONTEXT_MENU); - Controller.enableMapping("Default"); - enabled = false; - text = "Standard Built" - } - } Button { - text: root.xbox ? "XBox Mapping" : "XBox not found" - property bool built: false - enabled: root.xbox && !built + text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping") onClicked: { - var mapping = Controller.newMapping(); - mapping.from(xbox.A).to(standard.A); - mapping.from(xbox.B).to(standard.B); - mapping.from(xbox.X).to(standard.X); - mapping.from(xbox.Y).to(standard.Y); - mapping.from(xbox.Up).to(standard.DU); - mapping.from(xbox.Down).to(standard.DD); - mapping.from(xbox.Left).to(standard.DL); - mapping.from(xbox.Right).to(standard.Right); - mapping.from(xbox.LB).to(standard.LB); - mapping.from(xbox.RB).to(standard.RB); - mapping.from(xbox.LS).to(standard.LS); - mapping.from(xbox.RS).to(standard.RS); - mapping.from(xbox.Start).to(standard.Start); - mapping.from(xbox.Back).to(standard.Back); - mapping.from(xbox.LY).to(standard.LY); - mapping.from(xbox.LX).to(standard.LX); - mapping.from(xbox.RY).to(standard.RY); - mapping.from(xbox.RX).to(standard.RX); - mapping.from(xbox.LT).to(standard.LT); - mapping.from(xbox.RT).to(standard.RT); - mapping.enable(); - built = false; - text = "XBox Built" + + if (!root.testMapping) { + root.buildMapping() + } else { + root.toggleMapping(); + } } } - - Button { - text: root.hydra ? "Hydra Mapping" : "Hydra Not Found" - property bool built: false - enabled: root.hydra && !built - onClicked: { - var mapping = Controller.newMapping(); - mapping.from(hydra.LY).invert().to(standard.LY); - mapping.from(hydra.LX).to(standard.LX); - mapping.from(hydra.RY).invert().to(standard.RY); - mapping.from(hydra.RX).to(standard.RX); - mapping.from(hydra.LT).to(standard.LT); - mapping.from(hydra.RT).to(standard.RT); - mapping.enable(); - built = false; - text = "Hydra Built" - } - } - - Button { - text: "Test Mapping" - onClicked: { - var mapping = Controller.newMapping(); - // Inverting a value - mapping.from(standard.RY).invert().to(standard.RY); - mapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); - testMapping = mapping; - enabled = false - text = "Built" - } - } - - Button { - text: "Enable Mapping" - onClicked: root.testMapping.enable() - } - - Button { - text: "Disable Mapping" - onClicked: root.testMapping.disable() - } - - Button { - text: "Enable Mapping" - onClicked: print(Controller.getValue(root.xbox.LY)); - } } Row { @@ -170,25 +90,32 @@ HifiControls.VrDialog { ScrollingGraph { controlId: Controller.Actions.Yaw label: "Yaw" - min: -3.0 - max: 3.0 - size: 128 + min: -2.0 + max: 2.0 + size: 64 } ScrollingGraph { - controlId: Controller.Actions.YAW_LEFT + controlId: Controller.Actions.YawLeft label: "Yaw Left" - min: -3.0 - max: 3.0 - size: 128 + min: -2.0 + max: 2.0 + size: 64 } ScrollingGraph { - controlId: Controller.Actions.YAW_RIGHT + controlId: Controller.Actions.YawRight label: "Yaw Right" - min: -3.0 - max: 3.0 - size: 128 + min: -2.0 + max: 2.0 + size: 64 + } + ScrollingGraph { + controlId: Controller.Actions.StepYaw + label: "StepYaw" + min: -2.0 + max: 2.0 + size: 64 } } } diff --git a/interface/resources/qml/controller/Standard.qml b/interface/resources/qml/controller/Standard.qml index cee79fe50c..45e4febfa2 100644 --- a/interface/resources/qml/controller/Standard.qml +++ b/interface/resources/qml/controller/Standard.qml @@ -29,7 +29,7 @@ Item { // Left primary ToggleButton { x: parent.width - width; y: parent.height - height; - controlId: root.device.RB + controlId: root.device.RightPrimaryThumb width: 16 * root.scale; height: 16 * root.scale } } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 7954ab5522..b0d2d24edf 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -42,6 +42,12 @@ namespace controller { makeAxisPair(Action::ROLL, "Roll"), makeAxisPair(Action::PITCH, "Pitch"), makeAxisPair(Action::YAW, "Yaw"), + makeAxisPair(Action::STEP_YAW, "StepYaw"), + makeAxisPair(Action::STEP_PITCH, "StepPitch"), + makeAxisPair(Action::STEP_ROLL, "StepRoll"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), @@ -67,7 +73,6 @@ namespace controller { makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), - // Deprecated aliases // FIXME remove after we port all scripts makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 77a772de9e..47f04141f3 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -27,6 +27,16 @@ enum class Action { ROTATE_Y, YAW = ROTATE_Y, ROTATE_Z, ROLL = ROTATE_Z, + STEP_YAW, + // FIXME does this have a use case? + STEP_PITCH, + // FIXME does this have a use case? + STEP_ROLL, + + STEP_TRANSLATE_X, + STEP_TRANSLATE_Y, + STEP_TRANSLATE_Z, + TRANSLATE_CAMERA_Z, NUM_COMBINED_AXES, diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index d80952a5d9..ae806ed613 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -29,7 +29,6 @@ namespace controller { // Default contruct allocate the poutput size with the current hardcoded action channels controller::UserInputMapper::UserInputMapper() { - _activeMappings.push_back(_defaultMapping); _standardController = std::make_shared(); registerDevice(new ActionsDevice()); registerDevice(_standardController.get()); @@ -317,6 +316,7 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) { } void UserInputMapper::registerDevice(InputDevice* device) { + Locker locker(_lock); if (device->_deviceID == Input::INVALID_DEVICE) { device->_deviceID = getFreeDeviceID(); } @@ -354,13 +354,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { auto mapping = loadMapping(device->getDefaultMappingConfig()); if (mapping) { _mappingsByDevice[deviceID] = mapping; - auto& defaultRoutes = _defaultMapping->routes; - - // New routes for a device get injected IN FRONT of existing routes. Routes - // are processed in order so this ensures that the standard -> action processing - // takes place after all of the hardware -> standard or hardware -> action processing - // because standard -> action is the first set of routes added. - defaultRoutes.insert(defaultRoutes.begin(), mapping->routes.begin(), mapping->routes.end()); + enableMapping(mapping); } emit hardwareChanged(); @@ -368,6 +362,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { // FIXME remove the associated device mappings void UserInputMapper::removeDevice(int deviceID) { + Locker locker(_lock); auto proxyEntry = _registeredDevices.find(deviceID); if (_registeredDevices.end() == proxyEntry) { qCWarning(controllers) << "Attempted to remove unknown device " << deviceID; @@ -376,15 +371,7 @@ void UserInputMapper::removeDevice(int deviceID) { auto proxy = proxyEntry->second; auto mappingsEntry = _mappingsByDevice.find(deviceID); if (_mappingsByDevice.end() != mappingsEntry) { - const auto& mapping = mappingsEntry->second; - const auto& deviceRoutes = mapping->routes; - std::set routeSet(deviceRoutes.begin(), deviceRoutes.end()); - - auto& defaultRoutes = _defaultMapping->routes; - std::remove_if(defaultRoutes.begin(), defaultRoutes.end(), [&](Route::Pointer route)->bool { - return routeSet.count(route) != 0; - }); - + disableMapping(mappingsEntry->second); _mappingsByDevice.erase(mappingsEntry); } @@ -395,6 +382,7 @@ void UserInputMapper::removeDevice(int deviceID) { DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { + Locker locker(_lock); auto device = _registeredDevices.find(input.getDevice()); if (device != _registeredDevices.end()) { return (device->second); @@ -404,6 +392,7 @@ DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { } QString UserInputMapper::getDeviceName(uint16 deviceID) { + Locker locker(_lock); if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { return _registeredDevices[deviceID]->_name; } @@ -411,6 +400,7 @@ QString UserInputMapper::getDeviceName(uint16 deviceID) { } int UserInputMapper::findDevice(QString name) const { + Locker locker(_lock); for (auto device : _registeredDevices) { if (device.second->_name == name) { return device.first; @@ -420,6 +410,7 @@ int UserInputMapper::findDevice(QString name) const { } QVector UserInputMapper::getDeviceNames() { + Locker locker(_lock); QVector result; for (auto device : _registeredDevices) { QString deviceName = device.second->_name.split(" (")[0]; @@ -433,6 +424,7 @@ int UserInputMapper::findAction(const QString& actionName) const { } Input UserInputMapper::findDeviceInput(const QString& inputName) const { + Locker locker(_lock); // Split the full input name as such: deviceName.inputName auto names = inputName.split('.'); @@ -472,6 +464,7 @@ void fixBisectedAxis(float& full, float& negative, float& positive) { } void UserInputMapper::update(float deltaTime) { + Locker locker(_lock); // Reset the axis state for next loop for (auto& channel : _actionStates) { channel = 0.0f; @@ -505,11 +498,13 @@ void UserInputMapper::update(float deltaTime) { } Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { + Locker locker(_lock); auto iterator = _registeredDevices.find(deviceID); return iterator->second->getAvailabeInputs(); } QVector UserInputMapper::getAllActions() const { + Locker locker(_lock); QVector actions; for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { actions.append(Action(i)); @@ -518,6 +513,7 @@ QVector UserInputMapper::getAllActions() const { } QString UserInputMapper::getActionName(Action action) const { + Locker locker(_lock); for (auto actionPair : getActionInputs()) { if (actionPair.first.channel == toInt(action)) { return actionPair.second; @@ -528,6 +524,7 @@ QString UserInputMapper::getActionName(Action action) const { QVector UserInputMapper::getActionNames() const { + Locker locker(_lock); QVector result; for (auto actionPair : getActionInputs()) { result << actionPair.second; @@ -645,74 +642,87 @@ void UserInputMapper::runMappings() { } // Now process the current values for each level of the stack - for (auto& mapping : _activeMappings) { - for (const auto& route : mapping->routes) { - if (route->conditional) { - if (!route->conditional->satisfied()) { - continue; - } - } + for (const auto& route : _deviceRoutes) { + if (!route) { + continue; + } + applyRoute(route); + } - auto source = route->source; - if (_overrides.count(source)) { - source = _overrides[source]; - } + for (const auto& route : _standardRoutes) { + if (!route) { + continue; + } + applyRoute(route); + } - // 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 - // and someone else wires it to CONTEXT_MENU, I don't want both to occur when - // I press the button. The exception is if I'm wiring a control back to itself - // in order to adjust my interface, like inverting the Y axis on an analog stick - if (!source->readable()) { - continue; - } +} - auto input = source->getInput(); - float value = source->value(); - if (value != 0.0) { - int i = 0; - } - - auto destination = route->destination; - // THis could happen if the route destination failed to create - // FIXME: Maybe do not create the route if the destination failed and avoid this case ? - if (!destination) { - continue; - } - - // FIXME?, should come before or after the override logic? - if (!destination->writeable()) { - continue; - } - - // Only consume the input if the route isn't a loopback. - // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` - bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT); - // Each time we loop back we re-write the override - if (loopback) { - _overrides[source] = destination = std::make_shared(source->getInput()); - } - - // Fetch the value, may have been overriden by previous loopback routes - if (source->isPose()) { - Pose value = getPose(source); - // no filters yet for pose - destination->apply(value, Pose(), source); - } else { - // Fetch the value, may have been overriden by previous loopback routes - float value = getValue(source); - - // Apply each of the filters. - for (const auto& filter : route->filters) { - value = filter->apply(value); - } - - destination->apply(value, 0, source); - } +void UserInputMapper::applyRoute(const Route::Pointer& route) { + if (route->conditional) { + if (!route->conditional->satisfied()) { + return; } } + auto source = route->source; + if (_overrides.count(source)) { + source = _overrides[source]; + } + + // 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 + // and someone else wires it to CONTEXT_MENU, I don't want both to occur when + // I press the button. The exception is if I'm wiring a control back to itself + // in order to adjust my interface, like inverting the Y axis on an analog stick + if (!source->readable()) { + return; + } + + + auto input = source->getInput(); + float value = source->value(); + if (value != 0.0) { + int i = 0; + } + + auto destination = route->destination; + // THis could happen if the route destination failed to create + // FIXME: Maybe do not create the route if the destination failed and avoid this case ? + if (!destination) { + return; + } + + // FIXME?, should come before or after the override logic? + if (!destination->writeable()) { + return; + } + + // Only consume the input if the route isn't a loopback. + // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` + bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT); + // Each time we loop back we re-write the override + if (loopback) { + _overrides[source] = destination = std::make_shared(source->getInput()); + } + + // Fetch the value, may have been overriden by previous loopback routes + if (source->isPose()) { + Pose value = getPose(source); + // no filters yet for pose + destination->apply(value, Pose(), source); + } else { + // Fetch the value, may have been overriden by previous loopback routes + float value = getValue(source); + + // Apply each of the filters. + for (const auto& filter : route->filters) { + value = filter->apply(value); + } + + destination->apply(value, 0, source); + } } Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { @@ -744,6 +754,7 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { } Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const { + Locker locker(_lock); auto iterator = _endpointsByInput.find(inputId); if (_endpointsByInput.end() == iterator) { qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); @@ -767,6 +778,7 @@ Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first, Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { + Locker locker(_lock); if (_mappingsByName.count(mappingName)) { qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; } @@ -799,8 +811,8 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { // return result; //} - void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { + Locker locker(_lock); qCDebug(controllers) << "Attempting to enable mapping " << mappingName; auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { @@ -810,18 +822,14 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { auto mapping = iterator->second; if (enable) { - _activeMappings.push_front(mapping); + enableMapping(mapping); } else { - auto activeIterator = std::find(_activeMappings.begin(), _activeMappings.end(), mapping); - if (_activeMappings.end() == activeIterator) { - qCWarning(controllers) << "Attempted to disable inactive mapping " << mappingName; - return; - } - _activeMappings.erase(activeIterator); + disableMapping(mapping); } } float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { + Locker locker(_lock); auto valuesIterator = _overrides.find(endpoint); if (_overrides.end() != valuesIterator) { return valuesIterator->second->value(); @@ -854,6 +862,7 @@ Pose UserInputMapper::getPose(const Input& input) const { } Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { + Locker locker(_lock); if (jsonFile.isEmpty()) { return Mapping::Pointer(); } @@ -1102,6 +1111,36 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { return parseMapping(doc.object()); } + +void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { + Locker locker(_lock); + // New routes for a device get injected IN FRONT of existing routes. Routes + // are processed in order so this ensures that the standard -> action processing + // takes place after all of the hardware -> standard or hardware -> action processing + // because standard -> action is the first set of routes added. + for (auto route : mapping->routes) { + if (route->source->getInput().device == STANDARD_DEVICE) { + _standardRoutes.push_front(route); + } else { + _deviceRoutes.push_front(route); + } + } +} + +void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { + Locker locker(_lock); + const auto& deviceRoutes = mapping->routes; + std::set routeSet(deviceRoutes.begin(), deviceRoutes.end()); + + // FIXME this seems to result in empty route pointers... need to find a better way to remove them. + std::remove_if(_deviceRoutes.begin(), _deviceRoutes.end(), [&](Route::Pointer route)->bool { + return routeSet.count(route) != 0; + }); + std::remove_if(_standardRoutes.begin(), _standardRoutes.end(), [&](Route::Pointer route)->bool { + return routeSet.count(route) != 0; + }); +} + } #include "UserInputMapper.moc" diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 345bba8c2b..70cd227e05 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -119,10 +120,8 @@ namespace controller { void hardwareChanged(); protected: - virtual void runMappings(); // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } - InputDevice::Pointer _standardController; DevicesMap _registeredDevices; uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; @@ -142,6 +141,11 @@ namespace controller { 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; @@ -163,9 +167,14 @@ namespace controller { EndpointOverrideMap _overrides; MappingNameMap _mappingsByName; - Mapping::Pointer _defaultMapping{ std::make_shared("Default") }; MappingDeviceMap _mappingsByDevice; - MappingStack _activeMappings; + + Route::List _deviceRoutes; + Route::List _standardRoutes; + + using Locker = std::unique_lock; + + mutable std::recursive_mutex _lock; }; } From 044a28212d6b2430715c5d68243a6891b0a70b84 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 20:44:38 -0700 Subject: [PATCH 092/232] Wiring step yaw to the avatar --- interface/resources/qml/TestControllers.qml | 3 +- interface/src/Application.cpp | 20 ++-- interface/src/avatar/Avatar.h | 15 --- interface/src/avatar/MyAvatar.cpp | 120 +++++++++----------- interface/src/avatar/MyAvatar.h | 16 +++ 5 files changed, 78 insertions(+), 96 deletions(-) diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index f1b8640c02..54b3cbf655 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -27,7 +27,8 @@ HifiControls.VrDialog { testMapping = Controller.newMapping(); testMapping.from(standard.RY).invert().to(actions.Pitch); testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); - testMapping.from(standard.RX).to(actions.StepYaw); + // Step yaw takes a number of degrees + testMapping.from(standard.RX).scale(15.0).to(actions.StepYaw); } function toggleMapping() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4418f94b3a..3b54562770 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2718,15 +2718,13 @@ void Application::update(float deltaTime) { } // Transfer the user inputs to the driveKeys + // FIXME can we drop drive keys and just have the avatar read the action states directly? myAvatar->clearDriveKeys(); if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { if (!_controllerScriptingInterface->areActionsCaptured()) { - myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(controller::Action::LONGITUDINAL_FORWARD)); - myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(controller::Action::LONGITUDINAL_BACKWARD)); - myAvatar->setDriveKeys(UP, userInputMapper->getActionState(controller::Action::VERTICAL_UP)); - myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(controller::Action::VERTICAL_DOWN)); - myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(controller::Action::LATERAL_LEFT)); - myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(controller::Action::LATERAL_RIGHT)); + myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); + myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); + myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); if (deltaTime > FLT_EPSILON) { // For rotations what we really want are meausures of "angles per second" (in order to prevent // fps-dependent spin rates) so we need to scale the units of the controller contribution. @@ -2734,14 +2732,12 @@ void Application::update(float deltaTime) { // controllers to provide a delta_per_second value rather than a raw delta.) const float EXPECTED_FRAME_RATE = 60.0f; float timeFactor = EXPECTED_FRAME_RATE * deltaTime; - myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(controller::Action::PITCH_UP) / timeFactor); - myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(controller::Action::PITCH_DOWN) / timeFactor); - myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(controller::Action::YAW_LEFT) / timeFactor); - myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(controller::Action::YAW_RIGHT) / timeFactor); + myAvatar->setDriveKeys(PITCH, userInputMapper->getActionState(controller::Action::PITCH) / timeFactor); + myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor); + myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor); } } - myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(controller::Action::BOOM_IN)); - myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(controller::Action::BOOM_OUT)); + myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 9a46a145c2..6a1f216089 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -43,21 +43,6 @@ static const float BILLBOARD_DISTANCE = 5.56f; // meters extern const float CHAT_MESSAGE_SCALE; extern const float CHAT_MESSAGE_HEIGHT; -enum DriveKeys { - FWD = 0, - BACK, - LEFT, - RIGHT, - UP, - DOWN, - ROT_LEFT, - ROT_RIGHT, - ROT_UP, - ROT_DOWN, - BOOM_IN, - BOOM_OUT, - MAX_DRIVE_KEYS -}; enum ScreenTintLayer { SCREEN_TINT_BEFORE_LANDSCAPE = 0, diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5202138147..8ada0ca481 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -242,6 +242,7 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("transform"); updateOrientation(deltaTime); updatePosition(deltaTime); + _lastStepPulse = _thisStepPulse; } { @@ -1552,71 +1553,49 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { !cameraInsideHead()); } +static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second + void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float targetSpeed = 0.0f; - - // FIXME - this comfort mode code is a total hack, remove it when we have new input mapping - bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); - bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode(); - - if (!isHMDMode || !isComfortMode) { - targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED; - - if (targetSpeed != 0.0f) { - const float ROTATION_RAMP_TIMESCALE = 0.1f; - float blend = deltaTime / ROTATION_RAMP_TIMESCALE; - if (blend > 1.0f) { - blend = 1.0f; - } - _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; - } else if (_bodyYawDelta != 0.0f) { - // attenuate body rotation speed - const float ROTATION_DECAY_TIMESCALE = 0.05f; - float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; - if (attenuation < 0.0f) { - attenuation = 0.0f; - } - _bodyYawDelta *= attenuation; - - float MINIMUM_ROTATION_RATE = 2.0f; - if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { - _bodyYawDelta = 0.0f; - } + float targetSpeed = _driveKeys[YAW] * YAW_SPEED; + if (targetSpeed != 0.0f) { + const float ROTATION_RAMP_TIMESCALE = 0.1f; + float blend = deltaTime / ROTATION_RAMP_TIMESCALE; + if (blend > 1.0f) { + blend = 1.0f; } + _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; + } else if (_bodyYawDelta != 0.0f) { + // attenuate body rotation speed + const float ROTATION_DECAY_TIMESCALE = 0.05f; + float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; + if (attenuation < 0.0f) { + attenuation = 0.0f; + } + _bodyYawDelta *= attenuation; - // update body orientation by movement inputs - setOrientation(getOrientation() * - glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); - - } else { - // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll - // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another - // snap turn every half second. - _bodyYawDelta = 0.0f; - - static quint64 lastPulse = 0; - quint64 now = usecTimestampNow(); - quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second - - float driveLeft = _driveKeys[ROT_LEFT]; - float driveRight= _driveKeys[ROT_RIGHT]; - - if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) { - lastPulse = now; - - const float SNAP_TURN_DELTA = 15.0f; // degrees - float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f; - float turnAmount = direction * SNAP_TURN_DELTA; - - // update body orientation by movement inputs - setOrientation(getOrientation() * - glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f)))); - + float MINIMUM_ROTATION_RATE = 2.0f; + if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { + _bodyYawDelta = 0.0f; } } - getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); + float totalBodyYaw = _bodyYawDelta * deltaTime; + + + // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll + // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another + // snap turn every half second. + quint64 now = usecTimestampNow(); + if (_driveKeys[STEP_YAW] != 0.0f && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) { + _thisStepPulse = now; + totalBodyYaw += _driveKeys[STEP_YAW]; + } + + // update body orientation by movement inputs + setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); + + getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * PITCH_SPEED * deltaTime); if (qApp->getAvatarUpdater()->isHMDMode()) { glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); @@ -1676,15 +1655,20 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); glm::vec3 newLocalVelocity = localVelocity; - float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + - (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + - fabsf(_driveKeys[UP] - _driveKeys[DOWN]); - if (keyboardInput) { - // Compute keyboard input - glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; - glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; - glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP; + float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]); + quint64 now = usecTimestampNow(); + if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) { + _thisStepPulse = now; + } + float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]); + if (keyboardInput || (_thisStepPulse == now)) { + // Compute keyboard input + glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT; + glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT; + glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP; + + // FIXME how do I implement step translation as well? glm::vec3 direction = front + right + up; float directionLength = glm::length(direction); @@ -1734,7 +1718,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe } } - float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN]; + float boomChange = _driveKeys[ZOOM]; _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); @@ -1983,7 +1967,7 @@ void MyAvatar::clearDriveKeys() { } void MyAvatar::relayDriveKeysToCharacterController() { - if (_driveKeys[UP] > 0.0f) { + if (_driveKeys[TRANSLATE_Y] > 0.0f) { _characterController.jump(); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4a0ae514f6..64814974b2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -21,6 +21,20 @@ class ModelItemID; +enum DriveKeys { + TRANSLATE_X = 0, + TRANSLATE_Y, + TRANSLATE_Z, + YAW, + STEP_TRANSLATE_X, + STEP_TRANSLATE_Y, + STEP_TRANSLATE_Z, + STEP_YAW, + PITCH, + ZOOM, + MAX_DRIVE_KEYS +}; + enum eyeContactTarget { LEFT_EYE, RIGHT_EYE, @@ -376,6 +390,8 @@ private: AtRestDetector _hmdAtRestDetector; glm::vec3 _lastPosition; bool _lastIsMoving = false; + quint64 _lastStepPulse = 0; + quint64 _thisStepPulse = 0; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); From 9fd61907f51aa79d136f5cbf1cb6a7032b3654d8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 21 Oct 2015 20:50:07 -0700 Subject: [PATCH 093/232] Call back to Javascript asynchronously, so that we don't block and the script's engine doesn't have thread conflicts. --- interface/src/avatar/MyAvatar.h | 2 + interface/src/avatar/SkeletonModel.cpp | 1 - libraries/animation/src/AnimVariant.h | 6 ++- libraries/animation/src/AnimVariantMap.cpp | 7 ++- libraries/animation/src/Rig.cpp | 50 ++++++++------------ libraries/animation/src/Rig.h | 8 ++-- libraries/script-engine/src/ScriptEngine.cpp | 37 +++++++++++++-- libraries/script-engine/src/ScriptEngine.h | 5 ++ 8 files changed, 77 insertions(+), 39 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 49f6c33aca..1ce72cb33f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -118,6 +118,8 @@ public: Q_INVOKABLE void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _rig->addAnimationStateHandler(handler, propertiesList); } // Removes a handler previously added by addAnimationStateHandler. Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } + // Processes a handler result. Not really for user code, but used by invokeAnimationCallback. + Q_INVOKABLE void animationStateHandlerResult(QScriptValue handler, QScriptValue result) { _rig->animationStateHandlerResult(handler, result); } // get/set avatar data void saveData(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 5c98d4cd9b..003677bf90 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -112,7 +112,6 @@ const float PALM_PRIORITY = DEFAULT_PRIORITY; // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Head* head = _owningAvatar->getHead(); - _rig->cleanupAnimationStateHandler(); if (_owningAvatar->isMyAvatar()) { MyAvatar* myAvatar = static_cast(_owningAvatar); const FBXGeometry& geometry = _geometry->getFBXGeometry(); diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 0a91c82d80..2d140bfa52 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -158,12 +158,14 @@ public: void setTrigger(const QString& key) { _triggers.insert(key); } void clearTriggers() { _triggers.clear(); } + void clearMap() { _map.clear(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. - QScriptValue animVariantMapToScriptValue(QScriptEngine* engine); + QScriptValue animVariantMapToScriptValue(QScriptEngine* engine) const; // Side-effect us with the value of object's own properties. (No inherited properties.) void animVariantMapFromScriptValue(const QScriptValue& object); + void copyVariantsFrom(const AnimVariantMap& other); #ifdef NDEBUG void dump() const { @@ -203,4 +205,6 @@ protected: std::set _triggers; }; +Q_DECLARE_METATYPE(AnimVariantMap) + #endif // hifi_AnimVariant_h diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index f626e46c0f..0154c9698a 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -14,7 +14,7 @@ #include #include "AnimVariant.h" -QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) { +QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) const { QScriptValue target = engine->newObject(); for (auto& pair : _map) { switch (pair.second.getType()) { @@ -43,6 +43,11 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) } return target; } +void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { + for (auto& pair : other._map) { + _map[pair.first] = pair.second; + } +} void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { // POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types. // Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 215e0095c0..14aef88e2f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -605,40 +605,32 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastPosition = worldPosition; } -void Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { +// Allow script to add/remove handlers and report results, from within their thread. +// TODO: iterate multiple handlers, but with one shared arg. +// TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) +void Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread _stateHandlers = handler; } -void Rig::removeAnimationStateHandler(QScriptValue handler) { - _stateHandlersResultsToRemove = _stateHandlersResults; - _stateHandlers = _stateHandlersResults = QScriptValue(); +void Rig::removeAnimationStateHandler(QScriptValue handler) { // called in script thread + _stateHandlers = QScriptValue(); + QMutexLocker locker(&_stateMutex); // guarding access to results + _stateHandlersResults.clearMap(); // TODO: When we have multiple handlers, we'll need to clear only his handler's results. } -void Rig::cleanupAnimationStateHandler() { - if (!_stateHandlersResultsToRemove.isValid()) { - return; - } - QScriptValueIterator property(_stateHandlersResultsToRemove); - while (property.hasNext()) { - property.next(); - _animVars.unset(property.name()); - } - _stateHandlersResultsToRemove = QScriptValue(); +void Rig::animationStateHandlerResult(QScriptValue handler, QScriptValue result) { // called synchronously from script + // handler is currently ignored but might be used in storing individual results + QMutexLocker locker(&_stateMutex); + _stateHandlersResults.animVariantMapFromScriptValue(result); // Into our own copy. } -void Rig::updateAnimationStateHandlers() { - if (!_stateHandlers.isValid()) { - return; +void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread) + if (_stateHandlers.isValid()) { + // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture + // the state of _animVars and allow continued changes to _animVars in this thread without conflict. + QMetaObject::invokeMethod(_stateHandlers.engine(), "invokeAnimationCallback", Qt::QueuedConnection, + Q_ARG(QScriptValue, _stateHandlers), + Q_ARG(AnimVariantMap, _animVars)); } - // TODO: iterate multiple handlers, but with one shared arg. - // TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) - // TODO: check QScriptEngine::hasUncaughtException() - // TODO: call asynchronously (through a signal on script), so that each script is single threaded, and so we never block here. - // This will require inboundMaps to be kept in the list of per-handler data. - QScriptEngine* engine = _stateHandlers.engine(); - QScriptValue outboundMap = _animVars.animVariantMapToScriptValue(engine); - QScriptValueList args; - args << outboundMap; - _stateHandlersResults = _stateHandlers.call(QScriptValue(), args); - _animVars.animVariantMapFromScriptValue(_stateHandlersResults); - //qCDebug(animation) << _animVars.lookup("foo", QString("not set")); + QMutexLocker locker(&_stateMutex); // as we examine/copy most recently computed state, if any. (Typically an earlier invocation.) + _animVars.copyVariantsFrom(_stateHandlersResults); } void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 3f68deef1a..c29bb6ad04 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -202,7 +202,7 @@ public: bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); void removeAnimationStateHandler(QScriptValue handler); - void cleanupAnimationStateHandler(); + void animationStateHandlerResult(QScriptValue handler, QScriptValue result); bool getModelOffset(glm::vec3& modelOffsetOut) const; @@ -248,9 +248,9 @@ public: float _rightHandOverlayAlpha = 0.0f; private: - QScriptValue _stateHandlers {}; - QScriptValue _stateHandlersResults {}; - QScriptValue _stateHandlersResultsToRemove {}; + QScriptValue _stateHandlers; + AnimVariantMap _stateHandlersResults; + QMutex _stateMutex; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 76590f266b..0ec1a09d6d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -258,6 +258,15 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { } } +// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of +// invokeAnimationCallback requires that the type be registered. +static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { + return parameters.animVariantMapToScriptValue(engine); +} +static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) { + parameters.animVariantMapFromScriptValue(value); +} + void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once @@ -316,6 +325,7 @@ void ScriptEngine::init() { registerGlobalObject("Vec3", &_vec3Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("AnimationCache", DependencyManager::get().data()); + qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); @@ -718,6 +728,21 @@ void ScriptEngine::stop() { } } +// Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. +void ScriptEngine::invokeAnimationCallback(QScriptValue callback, AnimVariantMap parameters) { + checkThread(); + QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this); + QScriptValueList callingArguments; + callingArguments << javascriptParametgers; + QScriptValue result = callback.call(QScriptValue(), callingArguments); + // We want to give the result back to the rig, but we don't have the rig or the avatar. But the global does. + // This is sort of like going through DependencyManager.get. + QScriptValue resultHandler = globalObject().property("MyAvatar").property("animationStateHandlerResult"); + QScriptValueList resultArguments; + resultArguments << callback << result; + resultHandler.call(QScriptValue(), resultArguments); // Call it synchronously, on our own time and thread. +} + void ScriptEngine::timerFired() { QTimer* callingTimer = reinterpret_cast(sender()); QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); @@ -898,14 +923,20 @@ void ScriptEngine::load(const QString& loadFile) { } } -// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args -void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator) { +bool ScriptEngine::checkThread() const { if (QThread::currentThread() != thread()) { qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; assert(false); + return true; + } + return false; +} + +// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args +void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator) { + if (checkThread()) { return; } - if (!_registeredHandlers.contains(entityID)) { return; } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1d3986143a..77f1e2d5f1 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -142,6 +143,9 @@ public: // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } +public slots: + void invokeAnimationCallback(QScriptValue callback, AnimVariantMap parameters); + signals: void scriptLoaded(const QString& scriptFilename); void errorLoadingScript(const QString& scriptFilename); @@ -169,6 +173,7 @@ protected: bool _wantSignals = true; QHash _entityScripts; private: + bool checkThread() const; void init(); QString getFilename() const; void waitTillDoneRunning(); From afcec347ffab06a3ac9ce7a5367d2393bdde93f9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 21:35:19 -0700 Subject: [PATCH 094/232] Wiring yaw action to avatar --- interface/resources/qml/TestControllers.qml | 4 ++- interface/src/Application.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 33 ++++++++++++++++----- interface/src/avatar/MyAvatar.h | 2 +- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index 54b3cbf655..3d1e13c6e3 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -26,8 +26,10 @@ HifiControls.VrDialog { function buildMapping() { testMapping = Controller.newMapping(); testMapping.from(standard.RY).invert().to(actions.Pitch); - testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); + //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); // Step yaw takes a number of degrees + testMapping.from(standard.LB).invert().scale(15.0).to(actions.StepYaw); + testMapping.from(standard.RB).scale(15.0).to(actions.StepYaw); testMapping.from(standard.RX).scale(15.0).to(actions.StepYaw); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3b54562770..0a634425bc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2732,7 +2732,7 @@ void Application::update(float deltaTime) { // controllers to provide a delta_per_second value rather than a raw delta.) const float EXPECTED_FRAME_RATE = 60.0f; float timeFactor = EXPECTED_FRAME_RATE * deltaTime; - myAvatar->setDriveKeys(PITCH, userInputMapper->getActionState(controller::Action::PITCH) / timeFactor); + myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor); myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor); myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8ada0ca481..d822d37055 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -53,6 +53,7 @@ using namespace std; +static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const float YAW_SPEED = 150.0f; // degrees/sec const float PITCH_SPEED = 100.0f; // degrees/sec @@ -240,9 +241,31 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("transform"); + bool stepAction = false; + // When there are no step values, we zero out the last step pulse. + // This allows a user to do faster snapping by tapping a control + for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { + if (_driveKeys[i] != 0.0f) { + stepAction = true; + } + } + quint64 now = usecTimestampNow(); + quint64 pulseDeltaTime = now - _lastStepPulse; + if (!stepAction) { + _lastStepPulse = 0; + } + + if (stepAction && pulseDeltaTime > COMFORT_MODE_PULSE_TIMING) { + _pulseUpdate = true; + } + updateOrientation(deltaTime); updatePosition(deltaTime); - _lastStepPulse = _thisStepPulse; + + if (_pulseUpdate) { + _lastStepPulse = now; + _pulseUpdate = false; + } } { @@ -1553,8 +1576,6 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { !cameraInsideHead()); } -static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second - void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys float targetSpeed = _driveKeys[YAW] * YAW_SPEED; @@ -1588,7 +1609,6 @@ void MyAvatar::updateOrientation(float deltaTime) { // snap turn every half second. quint64 now = usecTimestampNow(); if (_driveKeys[STEP_YAW] != 0.0f && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) { - _thisStepPulse = now; totalBodyYaw += _driveKeys[STEP_YAW]; } @@ -1657,18 +1677,17 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe glm::vec3 newLocalVelocity = localVelocity; float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]); quint64 now = usecTimestampNow(); + // FIXME how do I implement step translation as well? if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) { - _thisStepPulse = now; } float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]); - if (keyboardInput || (_thisStepPulse == now)) { + if (keyboardInput) { // Compute keyboard input glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT; glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT; glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP; - // FIXME how do I implement step translation as well? glm::vec3 direction = front + right + up; float directionLength = glm::length(direction); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 64814974b2..c80a855149 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -391,7 +391,7 @@ private: glm::vec3 _lastPosition; bool _lastIsMoving = false; quint64 _lastStepPulse = 0; - quint64 _thisStepPulse = 0; + bool _pulseUpdate { false }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); From 3e7364608bbc7e27283d93767e996d515b975832 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 22 Oct 2015 09:17:29 -0700 Subject: [PATCH 095/232] make handControllerGrab.js work with new API --- examples/controllers/handControllerGrab.js | 22 ++++++++++--------- interface/resources/controllers/standard.json | 13 ++--------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 80fb4c8e40..17d16d1718 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -245,7 +245,7 @@ function MyController(hand, triggerAction) { }; this.updateSmoothedTrigger = function() { - var triggerValue = Controller.getActionValue(this.triggerAction); + var triggerValue = Controller.getValue(this.triggerAction); // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); @@ -260,7 +260,7 @@ function MyController(hand, triggerAction) { }; this.triggerSqueezed = function() { - var triggerValue = Controller.getActionValue(this.triggerAction); + var triggerValue = Controller.getValue(this.triggerAction); return triggerValue > TRIGGER_ON_VALUE; }; @@ -402,8 +402,9 @@ function MyController(hand, triggerAction) { this.distanceHolding = function() { - var handControllerPosition = Controller.getSpatialControlPosition(this.palm); - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity", "ignoreForCollisions"]); var now = Date.now(); @@ -452,8 +453,9 @@ function MyController(hand, triggerAction) { } var handPosition = this.getHandPosition(); - var handControllerPosition = Controller.getSpatialControlPosition(this.palm); - var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; + var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); @@ -595,7 +597,7 @@ function MyController(hand, triggerAction) { } - this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip); + this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;; this.currentObjectTime = Date.now(); }; @@ -613,7 +615,7 @@ function MyController(hand, triggerAction) { // of it's actual offset, let's try imparting a velocity which is at a fixed radius // from the palm. - var handControllerPosition = Controller.getSpatialControlPosition(this.tip); + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; var now = Date.now(); var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters @@ -856,8 +858,8 @@ function MyController(hand, triggerAction) { }; } -var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK")); -var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); +var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); +var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); function update() { rightController.update(); diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index ef6668ec07..2067d98380 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -12,17 +12,8 @@ { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, - { - "from": "Standard.RT", - "to": "Actions.BOOM_IN", - "filters": [ { "type": "scale", "scale": 0.1 } ] - }, - - { - "from": "Standard.LT", - "to": "Actions.BOOM_OUT", - "filters": [ { "type": "scale", "scale": 0.1 } ] - }, + { "from": "Standard.LT", "to": "Actions.LEFT_HAND_CLICK" }, + { "from": "Standard.RT", "to": "Actions.RIGHT_HAND_CLICK" }, { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } From 4938e5ea8421c2fa5aa29df363f3492a0d9cabdf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 23:58:36 -0700 Subject: [PATCH 096/232] Fixing function based routes, makeAxis --- examples/tests/controllerInterfaceTest.js | 21 ++++++++++++++++++- interface/resources/qml/TestControllers.qml | 7 +++++-- .../controllers/src/controllers/Input.cpp | 9 ++++---- libraries/controllers/src/controllers/Input.h | 2 +- .../src/controllers/UserInputMapper.cpp | 9 +++++++- .../controllers/impl/MappingBuilderProxy.cpp | 10 ++++----- .../controllers/impl/MappingBuilderProxy.h | 6 +++--- 7 files changed, 46 insertions(+), 18 deletions(-) diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index 48ad8f0879..35a92daa6b 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -4,10 +4,29 @@ ControllerTest = function() { this.mappingEnabled = false; this.mapping = Controller.newMapping(); this.mapping.from(standard.RX).to(actions.StepYaw); + this.mapping.from(standard.RY).invert().to(actions.Pitch); + + var testMakeAxis = false; + if (testMakeAxis) { + this.mapping.makeAxis(standard.LB, standard.RB).pulse(0.25).scale(40.0).to(actions.StepYaw); + } + + var testStepYaw = false; + if (!testMakeAxis && testStepYaw){ + this.mapping.from(standard.LB).pulse(0.10).invert().scale(40.0).to(actions.StepYaw); + this.mapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw); + } + + var testFunctionSource = false; + if (testFunctionSource) { + this.mapping.fromFunction(function(){ + return Math.sin(Date.now() / 250); + }).to(actions.Yaw); + } + this.mapping.enable(); this.mappingEnabled = true; - print("Actions"); for (var prop in Controller.Actions) { print("\t" + prop); diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index 3d1e13c6e3..f967b8bf9c 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -26,10 +26,13 @@ HifiControls.VrDialog { function buildMapping() { testMapping = Controller.newMapping(); testMapping.from(standard.RY).invert().to(actions.Pitch); + testMapping.fromQmlFunction(function(){ + return Math.sin(Date.now() / 250); + }).to(actions.Yaw); //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); // Step yaw takes a number of degrees - testMapping.from(standard.LB).invert().scale(15.0).to(actions.StepYaw); - testMapping.from(standard.RB).scale(15.0).to(actions.StepYaw); + testMapping.from(standard.LB).pulse(0.10).invert().scale(40.0).to(actions.StepYaw); + testMapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw); testMapping.from(standard.RX).scale(15.0).to(actions.StepYaw); } diff --git a/libraries/controllers/src/controllers/Input.cpp b/libraries/controllers/src/controllers/Input.cpp index 29d2fed617..4f645c3f95 100644 --- a/libraries/controllers/src/controllers/Input.cpp +++ b/libraries/controllers/src/controllers/Input.cpp @@ -10,10 +10,9 @@ namespace controller { - const Input Input::INVALID_INPUT = Input(UINT32_MAX); - const uint16_t Input::INVALID_DEVICE = INVALID_INPUT.getDevice(); - const uint16_t Input::INVALID_CHANNEL = INVALID_INPUT.getChannel(); - const uint16_t Input::INVALID_TYPE = (uint16_t)INVALID_INPUT.getType(); - + 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); } diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h index 6f997c9f91..7382d365ec 100644 --- a/libraries/controllers/src/controllers/Input.h +++ b/libraries/controllers/src/controllers/Input.h @@ -16,7 +16,7 @@ namespace controller { enum class ChannelType { - UNKNOWN = 0, + INVALID = 0, BUTTON = 1, AXIS, POSE, diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index ae806ed613..f1138479bf 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -145,10 +145,17 @@ void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); } +static const Input INVALID_STANDARD_INPUT = Input(UserInputMapper::STANDARD_DEVICE, Input::INVALID_CHANNEL, ChannelType::INVALID); + class CompositeEndpoint : public Endpoint, Endpoint::Pair { public: CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) - : Endpoint(Input::INVALID_INPUT), Pair(first, second) { } + : Endpoint(Input::INVALID_INPUT), Pair(first, second) { + if (first->getInput().device == UserInputMapper::STANDARD_DEVICE && + second->getInput().device == UserInputMapper::STANDARD_DEVICE) { + this->_input = INVALID_STANDARD_INPUT; + } + } virtual float value() { float result = first->value() * -1.0f + second->value(); diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 462a319a90..91b11115d9 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -26,13 +26,13 @@ QObject* MappingBuilderProxy::from(int input) { return from(sourceEndpoint); } -QObject* MappingBuilderProxy::from(const QJSValue& source) { +QObject* MappingBuilderProxy::fromQmlFunction(const QJSValue& source) { qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); auto sourceEndpoint = _parent.endpointFor(source); return from(sourceEndpoint); } -QObject* MappingBuilderProxy::from(const QScriptValue& source) { +QObject* MappingBuilderProxy::fromFunction(const QScriptValue& source) { qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); auto sourceEndpoint = _parent.endpointFor(source); return from(sourceEndpoint); @@ -49,9 +49,9 @@ QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { } } -QObject* MappingBuilderProxy::makeAxis(const QJSValue& source1, const QJSValue& source2) { - auto source1Endpoint = _parent.endpointFor(source1); - auto source2Endpoint = _parent.endpointFor(source2); +QObject* MappingBuilderProxy::makeAxis(int source1, int source2) { + auto source1Endpoint = _parent.endpointFor(Input(source1)); + auto source2Endpoint = _parent.endpointFor(Input(source2)); return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 07c1730836..06ffcd8ea3 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -33,9 +33,9 @@ public: : _parent(parent), _mapping(mapping) { } Q_INVOKABLE QObject* from(int sourceInput); - Q_INVOKABLE QObject* from(const QJSValue& source); - Q_INVOKABLE QObject* from(const QScriptValue& source); - Q_INVOKABLE QObject* makeAxis(const QJSValue& source1, const QJSValue& source2); + Q_INVOKABLE QObject* fromQmlFunction(const QJSValue& source); + Q_INVOKABLE QObject* fromFunction(const QScriptValue& source); + Q_INVOKABLE QObject* makeAxis(int source1, const int source2); Q_INVOKABLE QObject* enable(bool enable = true); Q_INVOKABLE QObject* disable() { return enable(false); } From 5d4cbfdacba62c81fb14df4c58e0b7dbffc8dfec Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 09:40:41 -0700 Subject: [PATCH 097/232] Resolving ambiguity between functions and inputs differently --- examples/tests/controllerInterfaceTest.js | 35 ++++++++++--------- interface/resources/qml/TestControllers.qml | 17 ++++----- .../controllers/impl/MappingBuilderProxy.cpp | 22 ++++++------ .../controllers/impl/MappingBuilderProxy.h | 9 ++--- .../controllers/impl/RouteBuilderProxy.cpp | 10 ++---- .../src/controllers/impl/RouteBuilderProxy.h | 7 ++-- 6 files changed, 49 insertions(+), 51 deletions(-) diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index 35a92daa6b..a4e0557c78 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -17,9 +17,9 @@ ControllerTest = function() { this.mapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw); } - var testFunctionSource = false; + var testFunctionSource = true; if (testFunctionSource) { - this.mapping.fromFunction(function(){ + this.mapping.from(function(){ return Math.sin(Date.now() / 250); }).to(actions.Yaw); } @@ -27,22 +27,25 @@ ControllerTest = function() { this.mapping.enable(); this.mappingEnabled = true; - print("Actions"); - for (var prop in Controller.Actions) { - print("\t" + prop); - } - print("Standard"); - for (var prop in Controller.Standard) { - print("\t" + prop); - } - print("Hardware"); - for (var prop in Controller.Hardware) { - print("\t" + prop); - for (var prop2 in Controller.Hardware[prop]) { - print("\t\t" + prop2); + var dumpInputs = false; + if (dumpInputs) { + print("Actions"); + for (var prop in Controller.Actions) { + print("\t" + prop); } + print("Standard"); + for (var prop in Controller.Standard) { + print("\t" + prop); + } + print("Hardware"); + for (var prop in Controller.Hardware) { + print("\t" + prop); + for (var prop2 in Controller.Hardware[prop]) { + print("\t\t" + prop2); + } + } + print("Done"); } - print("Done"); var that = this; Script.scriptEnding.connect(function() { diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index f967b8bf9c..a21735b3cc 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -25,15 +25,15 @@ HifiControls.VrDialog { function buildMapping() { testMapping = Controller.newMapping(); - testMapping.from(standard.RY).invert().to(actions.Pitch); - testMapping.fromQmlFunction(function(){ + testMapping.fromQml(standard.RY).invert().toQml(actions.Pitch); + testMapping.fromQml(function(){ return Math.sin(Date.now() / 250); - }).to(actions.Yaw); + }).toQml(actions.Yaw); //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); // Step yaw takes a number of degrees - testMapping.from(standard.LB).pulse(0.10).invert().scale(40.0).to(actions.StepYaw); - testMapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw); - testMapping.from(standard.RX).scale(15.0).to(actions.StepYaw); + testMapping.fromQml(standard.LB).pulse(0.10).invert().scale(40.0).toQml(actions.StepYaw); + testMapping.fromQml(standard.RB).pulse(0.10).scale(15.0).toQml(actions.StepYaw); + testMapping.fromQml(standard.RX).scale(15.0).toQml(actions.StepYaw); } function toggleMapping() { @@ -91,8 +91,9 @@ HifiControls.VrDialog { Hydra { device: root.hydra; width: 180 } } - Row { - spacing: 8 + Grid { + columns: 6 + spacing: 4 ScrollingGraph { controlId: Controller.Actions.Yaw label: "Yaw" diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp index 91b11115d9..1af3f271be 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp @@ -20,19 +20,13 @@ using namespace controller; -QObject* MappingBuilderProxy::from(int input) { - qCDebug(controllers) << "Creating new Route builder proxy from " << input; - auto sourceEndpoint = _parent.endpointFor(Input(input)); - return from(sourceEndpoint); -} - -QObject* MappingBuilderProxy::fromQmlFunction(const QJSValue& source) { +QObject* MappingBuilderProxy::fromQml(const QJSValue& source) { qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); auto sourceEndpoint = _parent.endpointFor(source); return from(sourceEndpoint); } -QObject* MappingBuilderProxy::fromFunction(const QScriptValue& source) { +QObject* MappingBuilderProxy::from(const QScriptValue& source) { qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); auto sourceEndpoint = _parent.endpointFor(source); return from(sourceEndpoint); @@ -49,9 +43,15 @@ QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { } } -QObject* MappingBuilderProxy::makeAxis(int source1, int source2) { - auto source1Endpoint = _parent.endpointFor(Input(source1)); - auto source2Endpoint = _parent.endpointFor(Input(source2)); +QObject* MappingBuilderProxy::makeAxisQml(const QJSValue& source1, const QJSValue& source2) { + auto source1Endpoint = _parent.endpointFor(source1); + auto source2Endpoint = _parent.endpointFor(source2); + return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); +} + +QObject* MappingBuilderProxy::makeAxis(const QScriptValue& source1, const QScriptValue& source2) { + auto source1Endpoint = _parent.endpointFor(source1); + auto source2Endpoint = _parent.endpointFor(source2); return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); } diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 06ffcd8ea3..93aa022647 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -32,10 +32,11 @@ public: MappingBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping) : _parent(parent), _mapping(mapping) { } - Q_INVOKABLE QObject* from(int sourceInput); - Q_INVOKABLE QObject* fromQmlFunction(const QJSValue& source); - Q_INVOKABLE QObject* fromFunction(const QScriptValue& source); - Q_INVOKABLE QObject* makeAxis(int source1, const int source2); + Q_INVOKABLE QObject* fromQml(const QJSValue& source); + Q_INVOKABLE QObject* makeAxisQml(const QJSValue& source1, const QJSValue& source2); + + Q_INVOKABLE QObject* from(const QScriptValue& source); + Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2); Q_INVOKABLE QObject* enable(bool enable = true); Q_INVOKABLE QObject* disable() { return enable(false); } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 14bcbca80e..186cf2e84e 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -20,13 +20,7 @@ using namespace controller; -void RouteBuilderProxy::to(int destinationInput) { - qCDebug(controllers) << "Completing route " << destinationInput; - auto destinationEndpoint = _parent.endpointFor(Input(destinationInput)); - return to(destinationEndpoint); -} - -void RouteBuilderProxy::to(const QJSValue& destination) { +void RouteBuilderProxy::toQml(const QJSValue& destination) { qCDebug(controllers) << "Completing route " << destination.toString(); auto destinationEndpoint = _parent.endpointFor(destination); return to(destinationEndpoint); @@ -45,7 +39,7 @@ void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { deleteLater(); } -QObject* RouteBuilderProxy::filter(const QJSValue& expression) { +QObject* RouteBuilderProxy::filterQml(const QJSValue& expression) { if (expression.isCallable()) { addFilter([=](float value) { QJSValue originalExpression = expression; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 1b66a3d996..6bceba995a 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -31,11 +31,10 @@ class RouteBuilderProxy : public QObject { RouteBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping, Route::Pointer route) : _parent(parent), _mapping(mapping), _route(route) { } - Q_INVOKABLE void to(int destination); - Q_INVOKABLE void to(const QJSValue& destination); - Q_INVOKABLE void to(const QScriptValue& destination); + Q_INVOKABLE void toQml(const QJSValue& destination); + Q_INVOKABLE QObject* filterQml(const QJSValue& expression); - Q_INVOKABLE QObject* filter(const QJSValue& expression); + Q_INVOKABLE void to(const QScriptValue& destination); Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); Q_INVOKABLE QObject* pulse(float interval); From 6cf0bdcffede8b783b7255b19c0e31505c8a38db Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 10:00:11 -0700 Subject: [PATCH 098/232] Testing function destination and fixing bug in rule ordering for multi-soure --- examples/tests/controllerInterfaceTest.js | 14 ++++++++++++- .../src/controllers/UserInputMapper.cpp | 21 +++++++++++++++---- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index a4e0557c78..0dccd1209a 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -1,11 +1,13 @@ ControllerTest = function() { var standard = Controller.Standard; var actions = Controller.Actions; + var xbox = Controller.Hardware.GamePad; this.mappingEnabled = false; this.mapping = Controller.newMapping(); this.mapping.from(standard.RX).to(actions.StepYaw); this.mapping.from(standard.RY).invert().to(actions.Pitch); + var testMakeAxis = false; if (testMakeAxis) { this.mapping.makeAxis(standard.LB, standard.RB).pulse(0.25).scale(40.0).to(actions.StepYaw); @@ -17,12 +19,22 @@ ControllerTest = function() { this.mapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw); } - var testFunctionSource = true; + var testFunctionSource = false; if (testFunctionSource) { this.mapping.from(function(){ return Math.sin(Date.now() / 250); }).to(actions.Yaw); } + + var testFunctionDest = true; + if (testFunctionDest) { + this.mapping.from(standard.DU).pulse(1.0).to(function(value){ + if (value != 0.0) { + print(value); + } + }); + + } this.mapping.enable(); this.mappingEnabled = true; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index f1138479bf..107b6f8192 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -195,7 +195,20 @@ class AnyEndpoint : public Endpoint { friend class UserInputMapper; public: using Pointer = std::shared_ptr; - AnyEndpoint() : Endpoint(Input::INVALID_INPUT) {} + AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) { + bool standard = true; + // Ensure if we're building a composite of standard devices the composite itself + // is treated as a standard device for rule processing order + for (auto endpoint : children) { + if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) { + standard = false; + break; + } + } + if (standard) { + this->_input = INVALID_STANDARD_INPUT; + } + } virtual float value() override { float result = 0; @@ -1020,15 +1033,15 @@ Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) { if (value.isArray()) { - AnyEndpoint::Pointer result = std::make_shared(); + Endpoint::List children; for (auto arrayItem : value.toArray()) { Endpoint::Pointer destination = parseEndpoint(arrayItem); if (!destination) { return Endpoint::Pointer(); } - result->_children.push_back(destination); + children.push_back(destination); } - return result; + return std::make_shared(children); } return parseEndpoint(value); From 67f2a0c6c803263171a98ab02588079beb1a4ba8 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 22 Oct 2015 11:39:51 -0700 Subject: [PATCH 099/232] CR feedback on mappings --- interface/resources/controllers/standard.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 2067d98380..5d6e8ce32f 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -12,10 +12,10 @@ { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, - { "from": "Standard.LT", "to": "Actions.LEFT_HAND_CLICK" }, - { "from": "Standard.RT", "to": "Actions.RIGHT_HAND_CLICK" }, + { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, + { "from": "Standard.RT", "to": "Actions.RightHandClick" }, - { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, - { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } + { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, + { "from": "Standard.RightHand", "to": "Actions.RightHand" } ] } From b5ccd49959bbb2d8706e6edbafb8d9c5a9e90595 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 11:42:50 -0700 Subject: [PATCH 100/232] Make ubuntu happy. --- libraries/animation/src/Rig.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c29bb6ad04..648a7f2c16 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -37,6 +37,7 @@ #define __hifi__Rig__ #include +#include #include #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS From 30429e8138776e3f56d2effc82972fb43dd26b3b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 11:43:22 -0700 Subject: [PATCH 101/232] Don't use late-breaking results that got reported after the handler was removed. --- libraries/animation/src/Rig.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 14aef88e2f..2a64114112 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -619,6 +619,9 @@ void Rig::removeAnimationStateHandler(QScriptValue handler) { // called in scrip void Rig::animationStateHandlerResult(QScriptValue handler, QScriptValue result) { // called synchronously from script // handler is currently ignored but might be used in storing individual results QMutexLocker locker(&_stateMutex); + if (!_stateHandlers.isValid()) { + return; // Don't use late-breaking results that got reported after the handler was removed. + } _stateHandlersResults.animVariantMapFromScriptValue(result); // Into our own copy. } void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread) From c826b48c92d189c80c6109183b018f2769a2ddf3 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 22 Oct 2015 15:05:59 -0700 Subject: [PATCH 102/232] fix airGuitar.js --- examples/controllers/hydra/airGuitar.js | 163 ++++++++++++++---------- 1 file changed, 95 insertions(+), 68 deletions(-) diff --git a/examples/controllers/hydra/airGuitar.js b/examples/controllers/hydra/airGuitar.js index 7fbbfc48bd..b286cc6084 100644 --- a/examples/controllers/hydra/airGuitar.js +++ b/examples/controllers/hydra/airGuitar.js @@ -32,6 +32,8 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst"; // Load sounds that will be played +var heyManWave = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav"); + var chords = new Array(); // Nylon string guitar chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); @@ -56,96 +58,121 @@ var NUM_GUITARS = 3; var guitarSelector = NUM_CHORDS; var whichChord = 1; -var leftHanded = true; +var leftHanded = false; +var strumHand, chordHand, strumTrigger, chordTrigger; if (leftHanded) { - var strumHand = 0; - var chordHand = 1; + strumHand = Controller.Standard.LeftHand; + chordHand = Controller.Standard.RightHand; + strumTrigger = Controller.Standard.LT; + chordTrigger = Controller.Standard.RT; + changeGuitar = Controller.Standard.RB; + chord1 = Controller.Standard.X; + chord2 = Controller.Standard.Y; + chord3 = Controller.Standard.A; + chord4 = Controller.Standard.B; } else { - var strumHand = 1; - var chordHand = 0; + strumHand = Controller.Standard.RightHand; + chordHand = Controller.Standard.LeftHand; + strumTrigger = Controller.Standard.RT; + chordTrigger = Controller.Standard.LT; + changeGuitar = Controller.Standard.LB; + chord1 = Controller.Standard.DU; // these may not be correct, maybe we should map directly to Hydra?? + chord2 = Controller.Standard.DD; + chord3 = Controller.Standard.DL; + chord4 = Controller.Standard.DR; } var lastPosition = { x: 0.0, y: 0.0, z: 0.0 }; -var soundPlaying = false; +var audioInjector = null; var selectorPressed = false; var position; MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0); function checkHands(deltaTime) { - for (var palm = 0; palm < 2; palm++) { - var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); - var volume = length(palmVelocity) / 5.0; - var position = Controller.getSpatialControlPosition(palm * 2 + 1); - var myPelvis = MyAvatar.position; - var trigger = Controller.getTriggerValue(strumHand); - var chord = Controller.getTriggerValue(chordHand); + var strumVelocity = Controller.getPoseValue(strumHand).velocity; + var volume = length(strumVelocity) / 5.0; - if (volume > 1.0) volume = 1.0; - if ((chord > 0.1) && soundPlaying.isPlaying) { - // If chord finger trigger pulled, stop current chord - print("stopped sound"); - soundPlaying.stop(); - } - - var BUTTON_COUNT = 6; - - // Change guitars if button FWD (5) pressed - if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) { - if (!selectorPressed) { - guitarSelector += NUM_CHORDS; - if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { - guitarSelector = 0; - } - selectorPressed = true; - } - } else { - selectorPressed = false; - } - - if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) { - whichChord = 1; - } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) { - whichChord = 2; - } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) { - whichChord = 3; - } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) { - whichChord = 4; - } - - if (palm == strumHand) { - - var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; - var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; - //printVector(position); - if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) || - ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){ - // If hand passes downward or upward through 'strings', and finger trigger pulled, play - playChord(position, volume); - } - lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1); - } + if (volume == 0.0) { + volume = 1.0; } + + var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition; + var myPelvis = MyAvatar.position; + var strumming = Controller.getValue(strumTrigger); + var chord = Controller.getValue(chordTrigger); + + if (volume > 1.0) volume = 1.0; + if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) { + // If chord finger trigger pulled, stop current chord + print("stopping chord because cord trigger pulled"); + audioInjector.stop(); + } + + // Change guitars if button FWD (5) pressed + if (Controller.getValue(changeGuitar)) { + print("changeGuitar:" + changeGuitar); + if (!selectorPressed) { + guitarSelector += NUM_CHORDS; + if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { + guitarSelector = 0; + } + selectorPressed = true; + } + } else { + selectorPressed = false; + } + //print("selectorPressed:" + selectorPressed); + + if (Controller.getValue(chord1)) { + whichChord = 1; + } else if (Controller.getValue(chord2)) { + whichChord = 2; + } else if (Controller.getValue(chord3)) { + whichChord = 3; + } else if (Controller.getValue(chord4)) { + whichChord = 4; + } + + var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; + var strummingHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; + + var strumNowAbove = strumHandPosition.y > strummingHeight; + var strumNowBelow = strumHandPosition.y <= strummingHeight; + var strumWasAbove = lastPosition.y > strummingHeight; + var strumWasBelow = lastPosition.y <= strummingHeight; + var strumUp = strumNowAbove && strumWasBelow; + var strumDown = strumNowBelow && strumWasAbove; + + if ((strumUp || strumDown) && (strumming > 0.1)) { + // If hand passes downward or upward through 'strings', and finger trigger pulled, play + playChord(strumHandPosition, volume); + } + lastPosition = strumHandPosition; } function playChord(position, volume) { - if (soundPlaying.isPlaying) { + if (audioInjector && audioInjector.isPlaying) { print("stopped sound"); - soundPlaying.stop(); + audioInjector.stop(); } - print("Played sound: " + whichChord + " at volume " + options.volume); - if (!soundPlaying) { - soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], { - position: position, - volume: volume - }); - } else { - soundPlaying.restart(); - } + print("Played sound: " + whichChord + " at volume " + volume); + if (!audioInjector) { + + // FIXME - we apparenlty broke RAW file playback, so we need WAV files for all these chords. In the mean + // time, we will just play the heyMan wave file for all chords + var chord = heyManWave; // chords[guitarSelector + whichChord]; + + audioInjector = Audio.playSound(chord, { position: position, volume: volume }); + print("audioInjector: " + JSON.stringify(audioInjector)); + } else { + print("audioInjector: " + JSON.stringify(audioInjector)); + audioInjector.restart(); + } } From 759e6525069991a04005910cc36b514ffa876122 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 15:13:14 -0700 Subject: [PATCH 103/232] Thread test per comments. --- libraries/animation/src/AnimVariantMap.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index 0154c9698a..219a44b644 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -11,10 +11,15 @@ #include #include +#include #include #include "AnimVariant.h" QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) const { + if (QThread::currentThread() != engine->thread()) { + qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); + return QScriptValue(); + } QScriptValue target = engine->newObject(); for (auto& pair : _map) { switch (pair.second.getType()) { From 1d0464ede5d118be676e8ea9daf9821ef9d6c88f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 15:15:10 -0700 Subject: [PATCH 104/232] Name change and thread checks per comments. --- interface/src/avatar/MyAvatar.h | 2 +- libraries/animation/src/Rig.cpp | 3 +- libraries/script-engine/src/ScriptEngine.cpp | 29 ++++++++++---------- libraries/script-engine/src/ScriptEngine.h | 3 +- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1ce72cb33f..4f32989bf5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -118,7 +118,7 @@ public: Q_INVOKABLE void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _rig->addAnimationStateHandler(handler, propertiesList); } // Removes a handler previously added by addAnimationStateHandler. Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } - // Processes a handler result. Not really for user code, but used by invokeAnimationCallback. + // Processes a handler result. Not really for user code, but used by callAnimationStateHandler. Q_INVOKABLE void animationStateHandlerResult(QScriptValue handler, QScriptValue result) { _rig->animationStateHandlerResult(handler, result); } // get/set avatar data diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2a64114112..0437e9cca8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -624,11 +624,12 @@ void Rig::animationStateHandlerResult(QScriptValue handler, QScriptValue result) } _stateHandlersResults.animVariantMapFromScriptValue(result); // Into our own copy. } + void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread) if (_stateHandlers.isValid()) { // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture // the state of _animVars and allow continued changes to _animVars in this thread without conflict. - QMetaObject::invokeMethod(_stateHandlers.engine(), "invokeAnimationCallback", Qt::QueuedConnection, + QMetaObject::invokeMethod(_stateHandlers.engine(), "callAnimationStateHandler", Qt::QueuedConnection, Q_ARG(QScriptValue, _stateHandlers), Q_ARG(AnimVariantMap, _animVars)); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0ec1a09d6d..5808340855 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -259,7 +259,7 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { } // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of -// invokeAnimationCallback requires that the type be registered. +// callAnimationStateHandler requires that the type be registered. static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { return parameters.animVariantMapToScriptValue(engine); } @@ -340,7 +340,7 @@ void ScriptEngine::init() { void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; + qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; #endif QMetaObject::invokeMethod(this, "registerValue", Q_ARG(const QString&, valueName), @@ -729,8 +729,16 @@ void ScriptEngine::stop() { } // Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. -void ScriptEngine::invokeAnimationCallback(QScriptValue callback, AnimVariantMap parameters) { - checkThread(); +void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters) { + if (QThread::currentThread() != thread()) { +#ifdef THREAD_DEBUGGING + qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; +#endif + QMetaObject::invokeMethod(this, "callAnimationStateHandler", + Q_ARG(QScriptValue, callback), + Q_ARG(AnimVariantMap, parameters)); + return; + } QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this); QScriptValueList callingArguments; callingArguments << javascriptParametgers; @@ -923,19 +931,12 @@ void ScriptEngine::load(const QString& loadFile) { } } -bool ScriptEngine::checkThread() const { +// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args +void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator) { if (QThread::currentThread() != thread()) { qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; assert(false); - return true; - } - return false; -} - -// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args -void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator) { - if (checkThread()) { - return; + return ; } if (!_registeredHandlers.contains(entityID)) { return; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 77f1e2d5f1..7500e1d776 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -144,7 +144,7 @@ public: ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } public slots: - void invokeAnimationCallback(QScriptValue callback, AnimVariantMap parameters); + void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters); signals: void scriptLoaded(const QString& scriptFilename); @@ -173,7 +173,6 @@ protected: bool _wantSignals = true; QHash _entityScripts; private: - bool checkThread() const; void init(); QString getFilename() const; void waitTillDoneRunning(); From ecc920199d6752671fabea491c24779c0b7b32f2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 15:24:24 -0700 Subject: [PATCH 105/232] Return id suitable for use with remover, per comments. --- interface/src/avatar/MyAvatar.h | 2 +- libraries/animation/src/Rig.cpp | 3 ++- libraries/animation/src/Rig.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 4f32989bf5..435e6af5ef 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -115,7 +115,7 @@ public: // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut) // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. // It is not specified in what order multiple handlers are called. - Q_INVOKABLE void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { _rig->addAnimationStateHandler(handler, propertiesList); } + Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); } // Removes a handler previously added by addAnimationStateHandler. Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } // Processes a handler result. Not really for user code, but used by callAnimationStateHandler. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0437e9cca8..b4113fd4a5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -608,8 +608,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // Allow script to add/remove handlers and report results, from within their thread. // TODO: iterate multiple handlers, but with one shared arg. // TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) -void Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread +QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread _stateHandlers = handler; + return handler; // suitable for giving to removeAnimationStateHandler } void Rig::removeAnimationStateHandler(QScriptValue handler) { // called in script thread _stateHandlers = QScriptValue(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 648a7f2c16..83f1a02b8a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -201,7 +201,7 @@ public: AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) - void addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); + QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); void removeAnimationStateHandler(QScriptValue handler); void animationStateHandlerResult(QScriptValue handler, QScriptValue result); From f0a4e25e895017a4fb75f593564669761fb09e16 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 15:25:14 -0700 Subject: [PATCH 106/232] Name change per comments. --- interface/src/avatar/AvatarManager.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index c71f2bc25b..ffc7cc8f92 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -53,9 +53,11 @@ public: Q_INVOKABLE void setLocalLights(const QVector& localLights); Q_INVOKABLE QVector getLocalLights() const; // Currently, your own avatar will be included as the null avatar id. - Q_INVOKABLE QVector getAvatars() const { return _avatarHash.keys().toVector(); } + Q_INVOKABLE QVector getAvatarIdentifiers() const { return _avatarHash.keys().toVector(); } // FIXME: see #6154 + Q_INVOKABLE QVector getAvatars() const { return getAvatarIdentifiers(); } // FIXME: remove before merge. Compatability for testing scripts. // Minor Bug: A bogus avatarID answers your own avatar. - Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID) const { return _avatarHash[avatarID].get(); } + Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID) const { return _avatarHash[avatarID].get(); } // FIXME: see #6154 + void getObjectsToDelete(VectorOfMotionStates& motionStates); void getObjectsToAdd(VectorOfMotionStates& motionStates); From 913842ac30575a208d7ed9c7ec0affc8b4e94eaa Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 15:31:17 -0700 Subject: [PATCH 107/232] Thread check, for consistency. --- libraries/animation/src/AnimVariantMap.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index 219a44b644..170662f092 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -54,6 +54,10 @@ void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { } } void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { + if (QThread::currentThread() != source.engine()->thread()) { + qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread(); + return; + } // POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types. // Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here, // we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if From 471f43899d288e8fcad0c480e0ddc5769988cc07 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 15:49:27 -0700 Subject: [PATCH 108/232] Moving aliases / bisected versions below main actions --- .../controllers/src/controllers/Actions.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index b0d2d24edf..a9bd32b1d8 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -48,18 +48,6 @@ namespace controller { makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), - makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), - makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), - makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), - makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"), - makeAxisPair(Action::VERTICAL_DOWN, "Down"), - makeAxisPair(Action::VERTICAL_UP, "Up"), - makeAxisPair(Action::YAW_LEFT, "YawLeft"), - makeAxisPair(Action::YAW_RIGHT, "YawRight"), - makeAxisPair(Action::PITCH_DOWN, "PitchDown"), - makeAxisPair(Action::PITCH_UP, "PitchUp"), - makeAxisPair(Action::BOOM_IN, "BoomIn"), - makeAxisPair(Action::BOOM_OUT, "BoomOut"), makePosePair(Action::LEFT_HAND, "LeftHand"), makePosePair(Action::RIGHT_HAND, "RightHand"), @@ -73,6 +61,20 @@ namespace controller { makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), + // Aliases and bisected versions + makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), + makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), + makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), + makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"), + makeAxisPair(Action::VERTICAL_DOWN, "Down"), + makeAxisPair(Action::VERTICAL_UP, "Up"), + makeAxisPair(Action::YAW_LEFT, "YawLeft"), + makeAxisPair(Action::YAW_RIGHT, "YawRight"), + makeAxisPair(Action::PITCH_DOWN, "PitchDown"), + makeAxisPair(Action::PITCH_UP, "PitchUp"), + makeAxisPair(Action::BOOM_IN, "BoomIn"), + makeAxisPair(Action::BOOM_OUT, "BoomOut"), + // Deprecated aliases // FIXME remove after we port all scripts makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), From 8c163a52e9a005dc3b027203c531dce81f190966 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 15:49:58 -0700 Subject: [PATCH 109/232] Attempting to fix log graphs --- interface/resources/qml/ScrollingGraph.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/resources/qml/ScrollingGraph.qml b/interface/resources/qml/ScrollingGraph.qml index 26ca9a61ff..55523a23f4 100644 --- a/interface/resources/qml/ScrollingGraph.qml +++ b/interface/resources/qml/ScrollingGraph.qml @@ -23,6 +23,11 @@ Rectangle { function update() { value = Controller.getValue(controlId); + if (log) { + var log = Math.log(10) / Math.log(Math.abs(value)); + var sign = Math.sign(value); + value = log * sign; + } canvas.requestPaint(); } From d1da2f5aab07ec445cdbbce323b6ed139e2b5375 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 15:51:09 -0700 Subject: [PATCH 110/232] Adding more items to the standard controller abstraction --- .../src/controllers/StandardController.cpp | 20 +++++++++++++++---- .../src/controllers/StandardControls.h | 8 ++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 5734174284..fc62f85b81 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -73,10 +73,26 @@ void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) { availableInputs.append(makePair(LT, "LT")); availableInputs.append(makePair(RT, "RT")); + + // Finger abstractions + availableInputs.append(makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb")); + availableInputs.append(makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb")); + availableInputs.append(makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb")); + availableInputs.append(makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb")); + + availableInputs.append(makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex")); + availableInputs.append(makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex")); + availableInputs.append(makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex")); + availableInputs.append(makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex")); + + availableInputs.append(makePair(LEFT_GRIP, "LeftGrip")); + availableInputs.append(makePair(RIGHT_GRIP, "RightGrip")); + // Poses availableInputs.append(makePair(LEFT_HAND, "LeftHand")); availableInputs.append(makePair(RIGHT_HAND, "RightHand")); + // Aliases, PlayStation style names availableInputs.append(makePair(LB, "L1")); availableInputs.append(makePair(RB, "R1")); @@ -95,10 +111,6 @@ void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) { availableInputs.append(makePair(DR, "Right")); - availableInputs.append(makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb")); - availableInputs.append(makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb")); - availableInputs.append(makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb")); - availableInputs.append(makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb")); return availableInputs; }; diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index b051f68c13..066a3e5e60 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -42,6 +42,14 @@ namespace controller { RIGHT_PRIMARY_THUMB, RIGHT_SECONDARY_THUMB, + LEFT_PRIMARY_INDEX, + LEFT_SECONDARY_INDEX, + RIGHT_PRIMARY_INDEX, + RIGHT_SECONDARY_INDEX, + + LEFT_GRIP, + RIGHT_GRIP, + NUM_STANDARD_BUTTONS }; From e40741b5cb52bcfad1a18934b5fe365d0e439029 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 15:52:10 -0700 Subject: [PATCH 111/232] Removing overrides / loopback support, adding route debugging --- libraries/controllers/src/controllers/Route.h | 2 + .../src/controllers/UserInputMapper.cpp | 64 +++++++++++-------- .../src/controllers/UserInputMapper.h | 1 - 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/Route.h index f290799482..5ad3d36628 100644 --- a/libraries/controllers/src/controllers/Route.h +++ b/libraries/controllers/src/controllers/Route.h @@ -24,6 +24,8 @@ namespace controller { Endpoint::Pointer destination; Conditional::Pointer conditional; Filter::List filters; + QString json; + bool debug { false }; using Pointer = std::shared_ptr; using List = std::list; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 107b6f8192..e73e4d0e68 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -655,7 +655,6 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { void UserInputMapper::runMappings() { static auto deviceNames = getDeviceNames(); - _overrides.clear(); for (auto endpointEntry : this->_endpointsByInput) { endpointEntry.second->reset(); @@ -680,55 +679,55 @@ void UserInputMapper::runMappings() { void UserInputMapper::applyRoute(const Route::Pointer& route) { + if (route->debug) { + qCDebug(controllers) << "Applying route " << route->json; + } + if (route->conditional) { if (!route->conditional->satisfied()) { + if (route->debug) { + qCDebug(controllers) << "Conditional failed"; + } return; } } auto source = route->source; - if (_overrides.count(source)) { - source = _overrides[source]; - } - // Endpoints can only be read once (though a given mapping can route them to + // 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 // and someone else wires it to CONTEXT_MENU, I don't want both to occur when // I press the button. The exception is if I'm wiring a control back to itself // in order to adjust my interface, like inverting the Y axis on an analog stick if (!source->readable()) { + if (route->debug) { + qCDebug(controllers) << "Source unreadable"; + } return; } - - auto input = source->getInput(); - float value = source->value(); - if (value != 0.0) { - int i = 0; - } - auto destination = route->destination; // THis could happen if the route destination failed to create // FIXME: Maybe do not create the route if the destination failed and avoid this case ? if (!destination) { + if (route->debug) { + qCDebug(controllers) << "Bad Destination"; + } return; } - // FIXME?, should come before or after the override logic? if (!destination->writeable()) { + if (route->debug) { + qCDebug(controllers) << "Destination unwritable"; + } return; } - // Only consume the input if the route isn't a loopback. - // This allows mappings like `mapping.from(xbox.RY).invert().to(xbox.RY);` - bool loopback = (source->getInput() == destination->getInput()) && (source->getInput() != Input::INVALID_INPUT); - // Each time we loop back we re-write the override - if (loopback) { - _overrides[source] = destination = std::make_shared(source->getInput()); - } - // Fetch the value, may have been overriden by previous loopback routes if (source->isPose()) { + if (route->debug) { + qCDebug(controllers) << "Applying pose"; + } Pose value = getPose(source); // no filters yet for pose destination->apply(value, Pose(), source); @@ -736,11 +735,18 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { // Fetch the value, may have been overriden by previous loopback routes float value = getValue(source); + if (route->debug) { + qCDebug(controllers) << "Value was " << value; + } // Apply each of the filters. for (const auto& filter : route->filters) { value = filter->apply(value); } + if (route->debug) { + qCDebug(controllers) << "Filtered value was " << value; + } + destination->apply(value, 0, source); } } @@ -850,11 +856,6 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { Locker locker(_lock); - auto valuesIterator = _overrides.find(endpoint); - if (_overrides.end() != valuesIterator) { - return valuesIterator->second->value(); - } - return endpoint->value(); } @@ -900,6 +901,7 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { static const QString JSON_NAME = QStringLiteral("name"); static const QString JSON_CHANNELS = QStringLiteral("channels"); static const QString JSON_CHANNEL_FROM = QStringLiteral("from"); +static const QString JSON_CHANNEL_DEBUG = QStringLiteral("debug"); static const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); static const QString JSON_CHANNEL_TO = QStringLiteral("to"); static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); @@ -1052,9 +1054,12 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { return Route::Pointer(); } - const auto& obj = value.toObject(); + auto obj = value.toObject(); Route::Pointer result = std::make_shared(); + + result->json = QString(QJsonDocument(obj).toJson()); result->source = parseSource(obj[JSON_CHANNEL_FROM]); + result->debug = obj[JSON_CHANNEL_DEBUG].toBool(); if (!result->source) { qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; return Route::Pointer(); @@ -1067,6 +1072,11 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { return Route::Pointer(); } + if (result->source == result->destination) { + qWarning() << "Loopback routes not supported " << obj; + return Route::Pointer(); + } + if (obj.contains(JSON_CHANNEL_WHEN)) { auto conditionalsValue = obj[JSON_CHANNEL_WHEN]; result->conditional = parseConditional(conditionalsValue); diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 70cd227e05..4bfedfcf1a 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -165,7 +165,6 @@ namespace controller { EndpointToInputMap _inputsByEndpoint; EndpointPairMap _compositeEndpoints; - EndpointOverrideMap _overrides; MappingNameMap _mappingsByName; MappingDeviceMap _mappingsByDevice; From a9aea9b47712f467d12a3fef40adf33653ddfe15 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 15:53:19 -0700 Subject: [PATCH 112/232] Working on TestController display --- interface/resources/qml/TestControllers.qml | 31 +++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/TestControllers.qml b/interface/resources/qml/TestControllers.qml index a21735b3cc..482616203f 100644 --- a/interface/resources/qml/TestControllers.qml +++ b/interface/resources/qml/TestControllers.qml @@ -91,8 +91,7 @@ HifiControls.VrDialog { Hydra { device: root.hydra; width: 180 } } - Grid { - columns: 6 + Row { spacing: 4 ScrollingGraph { controlId: Controller.Actions.Yaw @@ -117,13 +116,41 @@ HifiControls.VrDialog { max: 2.0 size: 64 } + ScrollingGraph { controlId: Controller.Actions.StepYaw label: "StepYaw" + min: -20.0 + max: 20.0 + size: 64 + } + } + + Row { + ScrollingGraph { + controlId: Controller.Actions.TranslateZ + label: "TranslateZ" min: -2.0 max: 2.0 size: 64 } + + ScrollingGraph { + controlId: Controller.Actions.Forward + label: "Forward" + min: -2.0 + max: 2.0 + size: 64 + } + + ScrollingGraph { + controlId: Controller.Actions.Backward + label: "Backward" + min: -2.0 + max: 2.0 + size: 64 + } + } } } // dialog From 2db87e0d2d094c0891f35a6eced4428e5af065d4 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 22 Oct 2015 15:55:36 -0700 Subject: [PATCH 113/232] Remove hydramove.js and expose the velocity and angular Velocity for hydra --- examples/controllers/hydra/hydraMove.js | 303 ------------------ .../src/input-plugins/SixenseManager.cpp | 20 +- .../src/input-plugins/SixenseManager.h | 2 +- 3 files changed, 18 insertions(+), 307 deletions(-) delete mode 100644 examples/controllers/hydra/hydraMove.js diff --git a/examples/controllers/hydra/hydraMove.js b/examples/controllers/hydra/hydraMove.js deleted file mode 100644 index 46715839c3..0000000000 --- a/examples/controllers/hydra/hydraMove.js +++ /dev/null @@ -1,303 +0,0 @@ -// -// hydraMove.js -// examples -// -// Created by Brad Hefta-Gaub on February 10, 2014 -// Updated by Philip Rosedale on September 8, 2014 -// -// Copyright 2014 High Fidelity, Inc. -// -// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement -// avatar flying through the hydra/controller joysticks -// -// The joysticks (on hydra) will drive the avatar much like a playstation controller. -// -// Pressing the '4' or the 'FWD' button and moving/banking the hand will allow you to move and fly. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var damping = 0.9; -var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }; -var joysticksCaptured = false; -var THRUST_CONTROLLER = 0; -var VIEW_CONTROLLER = 1; -var INITIAL_THRUST_MULTIPLIER = 1.0; -var THRUST_INCREASE_RATE = 1.05; -var MAX_THRUST_MULTIPLIER = 75.0; -var thrustMultiplier = INITIAL_THRUST_MULTIPLIER; -var grabDelta = { x: 0, y: 0, z: 0}; -var grabStartPosition = { x: 0, y: 0, z: 0}; -var grabDeltaVelocity = { x: 0, y: 0, z: 0}; -var grabStartRotation = { x: 0, y: 0, z: 0, w: 1}; -var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1}; -var grabbingWithRightHand = false; -var wasGrabbingWithRightHand = false; -var grabbingWithLeftHand = false; -var wasGrabbingWithLeftHand = false; - -var EPSILON = 0.000001; -var velocity = { x: 0, y: 0, z: 0}; -var THRUST_MAG_UP = 100.0; -var THRUST_MAG_DOWN = 100.0; -var THRUST_MAG_FWD = 150.0; -var THRUST_MAG_BACK = 100.0; -var THRUST_MAG_LATERAL = 150.0; -var THRUST_JUMP = 120.0; - -var YAW_MAG = 100.0; -var PITCH_MAG = 100.0; -var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD; -var JOYSTICK_YAW_MAG = YAW_MAG; -var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5; - - -var LEFT_PALM = 0; -var LEFT_BUTTON_4 = 4; -var LEFT_BUTTON_FWD = 5; -var RIGHT_PALM = 2; -var RIGHT_BUTTON_4 = 10; -var RIGHT_BUTTON_FWD = 11; - - - -function printVector(text, v, decimals) { - print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals)); -} - -var debug = false; -var RED_COLOR = { red: 255, green: 0, blue: 0 }; -var GRAY_COLOR = { red: 25, green: 25, blue: 25 }; -var defaultPosition = { x: 0, y: 0, z: 0}; -var RADIUS = 0.05; -var greenSphere = -1; -var redSphere = -1; - -function createDebugOverlay() { - - if (greenSphere == -1) { - greenSphere = Overlays.addOverlay("sphere", { - position: defaultPosition, - size: RADIUS, - color: GRAY_COLOR, - alpha: 0.75, - visible: true, - solid: true, - anchor: "MyAvatar" - }); - redSphere = Overlays.addOverlay("sphere", { - position: defaultPosition, - size: RADIUS, - color: RED_COLOR, - alpha: 0.5, - visible: true, - solid: true, - anchor: "MyAvatar" - }); - } -} - -function destroyDebugOverlay() { - if (greenSphere != -1) { - Overlays.deleteOverlay(greenSphere); - Overlays.deleteOverlay(redSphere); - greenSphere = -1; - redSphere = -1; - } -} - -function displayDebug() { - if (!(grabbingWithRightHand || grabbingWithLeftHand)) { - if (greenSphere != -1) { - destroyDebugOverlay(); - } - } else { - // update debug indicator - if (greenSphere == -1) { - createDebugOverlay(); - } - - var displayOffset = { x:0, y:0.5, z:-0.5 }; - - Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } ); - Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } ); - } -} - -function getJoystickPosition(palm) { - // returns CONTROLLER_ID position in avatar local frame - var invRotation = Quat.inverse(MyAvatar.orientation); - var palmWorld = Controller.getSpatialControlPosition(palm); - var palmRelative = Vec3.subtract(palmWorld, MyAvatar.position); - var palmLocal = Vec3.multiplyQbyV(invRotation, palmRelative); - return palmLocal; -} - -// Used by handleGrabBehavior() for managing the grab position changes -function getAndResetGrabDelta() { - var HAND_GRAB_SCALE_DISTANCE = 2.0; - var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE)); - grabDelta = { x: 0, y: 0, z: 0}; - var avatarRotation = MyAvatar.orientation; - var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1)); - return result; -} - -function getGrabRotation() { - var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation)); - return quatDiff; -} - -// When move button is pressed, process results -function handleGrabBehavior(deltaTime) { - // check for and handle grab behaviors - grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4); - grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4); - stoppedGrabbingWithLeftHand = false; - stoppedGrabbingWithRightHand = false; - - if (grabbingWithRightHand && !wasGrabbingWithRightHand) { - // Just starting grab, capture starting rotation - grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); - grabStartPosition = getJoystickPosition(RIGHT_PALM); - if (debug) printVector("start position", grabStartPosition, 3); - } - if (grabbingWithRightHand) { - grabDelta = Vec3.subtract(getJoystickPosition(RIGHT_PALM), grabStartPosition); - grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); - } - if (!grabbingWithRightHand && wasGrabbingWithRightHand) { - // Just ending grab, capture velocity - grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM); - stoppedGrabbingWithRightHand = true; - } - - if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) { - // Just starting grab, capture starting rotation - grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); - grabStartPosition = getJoystickPosition(LEFT_PALM); - if (debug) printVector("start position", grabStartPosition, 3); - } - - if (grabbingWithLeftHand) { - grabDelta = Vec3.subtract(getJoystickPosition(LEFT_PALM), grabStartPosition); - grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); - } - if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) { - // Just ending grab, capture velocity - grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM); - stoppedGrabbingWithLeftHand = true; - } - - grabbing = grabbingWithRightHand || grabbingWithLeftHand; - stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand; - - if (grabbing) { - - var headOrientation = MyAvatar.headOrientation; - var front = Quat.getFront(headOrientation); - var right = Quat.getRight(headOrientation); - var up = Quat.getUp(headOrientation); - - if (debug) { - printVector("grabDelta: ", grabDelta, 3); - } - - var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta))); - - var THRUST_GRAB_SCALING = 100000.0; - - var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime); - MyAvatar.addThrust(thrustFront); - var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime); - MyAvatar.addThrust(thrustRight); - var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime); - MyAvatar.addThrust(thrustUp); - - // add some rotation... - var deltaRotation = getGrabRotation(); - var PITCH_SCALING = 2.5; - var PITCH_DEAD_ZONE = 2.0; - var YAW_SCALING = 2.5; - var ROLL_SCALING = 2.0; - - var euler = Quat.safeEulerAngles(deltaRotation); - - // Adjust body yaw by roll from controller - var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) + - (euler.z * ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation); - MyAvatar.orientation = orientation; - - // Adjust head pitch from controller - var pitch = 0.0; - if (Math.abs(euler.x) > PITCH_DEAD_ZONE) { - pitch = (euler.x < 0.0) ? (euler.x + PITCH_DEAD_ZONE) : (euler.x - PITCH_DEAD_ZONE); - } - MyAvatar.headPitch = MyAvatar.headPitch + (pitch * PITCH_SCALING * deltaTime); - - // TODO: Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster) - - } - - wasGrabbingWithRightHand = grabbingWithRightHand; - wasGrabbingWithLeftHand = grabbingWithLeftHand; -} - -// Update for joysticks and move button -var THRUST_DEAD_ZONE = 0.1; -var ROTATE_DEAD_ZONE = 0.1; -function flyWithHydra(deltaTime) { - var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER); - - if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) { - if (thrustMultiplier < MAX_THRUST_MULTIPLIER) { - thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE); - } - var headOrientation = MyAvatar.headOrientation; - - var front = Quat.getFront(headOrientation); - var right = Quat.getRight(headOrientation); - var up = Quat.getUp(headOrientation); - - var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS * - thrustJoystickPosition.y * thrustMultiplier * deltaTime); - MyAvatar.addThrust(thrustFront); - var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS * - thrustJoystickPosition.x * thrustMultiplier * deltaTime); - MyAvatar.addThrust(thrustRight); - } else { - thrustMultiplier = INITIAL_THRUST_MULTIPLIER; - } - - // View Controller - var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER); - if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) { - - // change the body yaw based on our x controller - var orientation = MyAvatar.orientation; - var deltaOrientation = Quat.fromPitchYawRollDegrees(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0); - MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation); - - // change the headPitch based on our x controller - var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime); - MyAvatar.headPitch = newPitch; - } - handleGrabBehavior(deltaTime); - displayDebug(); - -} - -Script.update.connect(flyWithHydra); -Controller.captureJoystick(THRUST_CONTROLLER); -Controller.captureJoystick(VIEW_CONTROLLER); - -// Map keyPress and mouse move events to our callbacks -function scriptEnding() { - // re-enabled the standard application for touch events - Controller.releaseJoystick(THRUST_CONTROLLER); - Controller.releaseJoystick(VIEW_CONTROLLER); -} -Script.scriptEnding.connect(scriptEnding); - - diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3dc983cb34..29e60ca5ec 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -230,7 +230,8 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { if (!jointsCaptured) { // Rotation of Palm glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); - handlePoseEvent(position, rotation, left); + handlePoseEvent(deltaTime, position, rotation, left); + } else { _poseStateMap.clear(); } @@ -384,7 +385,7 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { } } -void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left) { +void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { #ifdef HAVE_SIXENSE // From ABOVE the sixense coordinate frame looks like this: // @@ -400,6 +401,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, boo // | // | // z + auto prevPose = _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND]; // Transform the measured position into body frame. position = _avatarRotation * (position + _avatarPosition); @@ -443,8 +445,20 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, boo // TODO: find a shortcut with fewer rotations. rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; + glm::vec3 velocity(0.0f); + glm::quat angularVelocity; - _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND] = controller::Pose(position, rotation); + if (prevPose.isValid() && deltaTime > std::numeric_limits::epsilon()) { + velocity = (position - prevPose.getTranslation()) / deltaTime; + + auto deltaRot = rotation * glm::conjugate(prevPose.getRotation()); + auto axis = glm::axis(deltaRot); + auto angle = glm::angle(deltaRot); + angularVelocity = glm::angleAxis(angle / deltaTime, axis); + + } + + _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND] = controller::Pose(position, rotation, velocity, angularVelocity); #endif // HAVE_SIXENSE } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 91cdb5f60e..368321e669 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -75,7 +75,7 @@ public slots: private: void handleButtonEvent(unsigned int buttons, bool left); void handleAxisEvent(float x, float y, float trigger, bool left); - void handlePoseEvent(glm::vec3 position, glm::quat rotation, bool left); + void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); void updateCalibration(void* controllers); From 51c8d48c8c34ab852af1db263443725e2e0d9a8e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 16:12:31 -0700 Subject: [PATCH 114/232] Fix broken conditional caused by invalid route ordering --- .../src/controllers/UserInputMapper.cpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index e73e4d0e68..2ed81aecba 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -655,7 +655,6 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { void UserInputMapper::runMappings() { static auto deviceNames = getDeviceNames(); - for (auto endpointEntry : this->_endpointsByInput) { endpointEntry.second->reset(); } @@ -674,7 +673,6 @@ void UserInputMapper::runMappings() { } applyRoute(route); } - } @@ -1148,26 +1146,28 @@ void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { // are processed in order so this ensures that the standard -> action processing // takes place after all of the hardware -> standard or hardware -> action processing // because standard -> action is the first set of routes added. - for (auto route : mapping->routes) { - if (route->source->getInput().device == STANDARD_DEVICE) { - _standardRoutes.push_front(route); - } else { - _deviceRoutes.push_front(route); - } - } + Route::List standardRoutes = mapping->routes; + standardRoutes.remove_if([](const Route::Pointer& value) { + return (value->source->getInput().device == STANDARD_DEVICE); + }); + _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end()); + + 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()); } void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { Locker locker(_lock); const auto& deviceRoutes = mapping->routes; std::set routeSet(deviceRoutes.begin(), deviceRoutes.end()); - - // FIXME this seems to result in empty route pointers... need to find a better way to remove them. - std::remove_if(_deviceRoutes.begin(), _deviceRoutes.end(), [&](Route::Pointer route)->bool { - return routeSet.count(route) != 0; + _deviceRoutes.remove_if([&](const Route::Pointer& value){ + return routeSet.count(value) != 0; }); - std::remove_if(_standardRoutes.begin(), _standardRoutes.end(), [&](Route::Pointer route)->bool { - return routeSet.count(route) != 0; + _standardRoutes.remove_if([&](const Route::Pointer& value) { + return routeSet.count(value) != 0; }); } From 0ad4634eac482dc3e68d57a78513573de3c9164f Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 22 Oct 2015 16:13:12 -0700 Subject: [PATCH 115/232] Check that the velocity information is passed correctly and fixed clap.js --- examples/clap.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/clap.js b/examples/clap.js index 4ec740e3f8..54ca937786 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -71,8 +71,12 @@ function maybePlaySound(deltaTime) { var palm2Position = MyAvatar.getRightPalmPosition(); var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position)); - var palm1Velocity = Controller.getSpatialControlVelocity(1); - var palm2Velocity = Controller.getSpatialControlVelocity(3); + var palmPose = Controller.getPoseValue(Controller.Standard.LeftHand); + + + + var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity; + var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity; var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity)); const CLAP_SPEED = 0.7; From 2b7ceffd643824b2abeb5dbd14819fd500a93bfc Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 16:23:09 -0700 Subject: [PATCH 116/232] Get rid of globalObject().property("MyAvatar").property("animationStateHandlerResult"). --- libraries/animation/src/AnimVariant.h | 2 ++ libraries/animation/src/Rig.cpp | 6 +++++- libraries/script-engine/src/ScriptEngine.cpp | 21 +++++++++++++------- libraries/script-engine/src/ScriptEngine.h | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 2d140bfa52..4190abfed8 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -205,6 +205,8 @@ protected: std::set _triggers; }; +typedef std::function AnimVariantResultHandler; +Q_DECLARE_METATYPE(AnimVariantResultHandler); Q_DECLARE_METATYPE(AnimVariantMap) #endif // hifi_AnimVariant_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b4113fd4a5..7ab39c2f34 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -628,11 +628,15 @@ void Rig::animationStateHandlerResult(QScriptValue handler, QScriptValue result) void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread) if (_stateHandlers.isValid()) { + auto handleResult = [this](QScriptValue handler, QScriptValue result) { + animationStateHandlerResult(handler, result); + }; // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture // the state of _animVars and allow continued changes to _animVars in this thread without conflict. QMetaObject::invokeMethod(_stateHandlers.engine(), "callAnimationStateHandler", Qt::QueuedConnection, Q_ARG(QScriptValue, _stateHandlers), - Q_ARG(AnimVariantMap, _animVars)); + Q_ARG(AnimVariantMap, _animVars), + Q_ARG(AnimVariantResultHandler, handleResult)); } QMutexLocker locker(&_stateMutex); // as we examine/copy most recently computed state, if any. (Typically an earlier invocation.) _animVars.copyVariantsFrom(_stateHandlersResults); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 5808340855..acfa0c027b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -260,12 +260,23 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of // callAnimationStateHandler requires that the type be registered. +// These two are meaningful, if we ever do want to use them... static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { return parameters.animVariantMapToScriptValue(engine); } static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) { parameters.animVariantMapFromScriptValue(value); } +// ... while these two are not. But none of the four are ever used. +static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, const AnimVariantResultHandler& resultHandler) { + qCCritical(scriptengine) << "Attempt to marshall result handler to javascript"; + assert(false); + return QScriptValue(); +} +static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) { + qCCritical(scriptengine) << "Attempt to marshall result handler from javascript"; + assert(false); +} void ScriptEngine::init() { if (_isInitialized) { @@ -326,6 +337,7 @@ void ScriptEngine::init() { registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("AnimationCache", DependencyManager::get().data()); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); + qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); @@ -729,7 +741,7 @@ void ScriptEngine::stop() { } // Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. -void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters) { +void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, AnimVariantResultHandler resultHandler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; @@ -743,12 +755,7 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM QScriptValueList callingArguments; callingArguments << javascriptParametgers; QScriptValue result = callback.call(QScriptValue(), callingArguments); - // We want to give the result back to the rig, but we don't have the rig or the avatar. But the global does. - // This is sort of like going through DependencyManager.get. - QScriptValue resultHandler = globalObject().property("MyAvatar").property("animationStateHandlerResult"); - QScriptValueList resultArguments; - resultArguments << callback << result; - resultHandler.call(QScriptValue(), resultArguments); // Call it synchronously, on our own time and thread. + resultHandler(callback, result); } void ScriptEngine::timerFired() { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7500e1d776..b6f736c846 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -144,7 +144,7 @@ public: ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } public slots: - void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters); + void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, AnimVariantResultHandler resultHandler); signals: void scriptLoaded(const QString& scriptFilename); From 92ddedd44b4d1487ea2e802351617136ab38cf0d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Oct 2015 16:44:15 -0700 Subject: [PATCH 117/232] Make msvc happy. --- libraries/animation/src/AnimVariant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 4190abfed8..7a80321f7b 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -12,6 +12,7 @@ #define hifi_AnimVariant_h #include +#include #include #include #include From 84cfeaec13f79615e788815b14ef937dc76a10d5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 17:01:06 -0700 Subject: [PATCH 118/232] Linux QT wants .h-less headers. --- libraries/animation/src/AnimVariantMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index 170662f092..59f10a96e0 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include "AnimVariant.h" From 3e4bada313c65e485a35d014ee11dad213b46ef2 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 22 Oct 2015 18:03:47 -0700 Subject: [PATCH 119/232] remove uneeded var --- examples/clap.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/clap.js b/examples/clap.js index 54ca937786..9b21075ae7 100644 --- a/examples/clap.js +++ b/examples/clap.js @@ -71,10 +71,6 @@ function maybePlaySound(deltaTime) { var palm2Position = MyAvatar.getRightPalmPosition(); var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position)); - var palmPose = Controller.getPoseValue(Controller.Standard.LeftHand); - - - var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity; var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity; var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity)); From eaaec516c27b5578f09c28d9093806c4796d1342 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Thu, 22 Oct 2015 18:41:57 -0700 Subject: [PATCH 120/232] fix controllerExample --- .../avatarcontrol/controllerExample.js | 47 ++++--------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/examples/example/avatarcontrol/controllerExample.js b/examples/example/avatarcontrol/controllerExample.js index 66a9e40c56..fe23ce0e8e 100644 --- a/examples/example/avatarcontrol/controllerExample.js +++ b/examples/example/avatarcontrol/controllerExample.js @@ -10,25 +10,19 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - // initialize our triggers var triggerPulled = new Array(); -var numberOfTriggers = Controller.getNumberOfTriggers(); -for (t = 0; t < numberOfTriggers; t++) { +var NUMBER_OF_TRIGGERS = 2; +for (t = 0; t < NUMBER_OF_TRIGGERS; t++) { triggerPulled[t] = false; } - +var triggers = new Array(); +triggers[0] = Controller.Standard.LT; +triggers[1] = Controller.Standard.RT; function checkController(deltaTime) { - var numberOfTriggers = Controller.getNumberOfTriggers(); - var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); - var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; var triggerToggled = false; - - // this is expected for hydras - if (numberOfTriggers == 2 && controllersPerTrigger == 2) { - for (var t = 0; t < numberOfTriggers; t++) { - var triggerValue = Controller.getTriggerValue(t); - + for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) { + var triggerValue = Controller.getValue(triggers[t]]); if (triggerPulled[t]) { // must release to at least 0.1 if (triggerValue < 0.1) { @@ -41,17 +35,14 @@ function checkController(deltaTime) { triggerToggled = true; } } - if (triggerToggled) { print("a trigger was toggled"); } } - } + } - // register the call back so it fires before each data send Script.update.connect(checkController); - function printKeyEvent(eventName, event) { print(eventName); print(" event.key=" + event.key); @@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) { } function keyPressEvent(event) { printKeyEvent("keyPressEvent", event); - if (event.text == "A") { print("the A key was pressed"); } @@ -72,10 +62,8 @@ function keyPressEvent(event) { print("the key was pressed"); } } - function keyReleaseEvent(event) { printKeyEvent("keyReleaseEvent", event); - if (event.text == "A") { print("the A key was released"); } @@ -83,11 +71,9 @@ function keyReleaseEvent(event) { print("the key was pressed"); } } - // Map keyPress and mouse move events to our callbacks Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent); - // prevent the A key from going through to the application Controller.captureKeyEvents({ text: "A" }); Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key @@ -95,8 +81,6 @@ Controller.captureKeyEvents({ text: " " }); Controller.captureKeyEvents({ text: "@", isMeta: true }); Controller.captureKeyEvents({ text: "page up" }); Controller.captureKeyEvents({ text: "page down" }); - - function printMouseEvent(eventName, event) { print(eventName); print(" event.x,y=" + event.x + ", " + event.y); @@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) { print(" event.isMeta=" + event.isMeta); print(" event.isAlt=" + event.isAlt); } - function mouseMoveEvent(event) { printMouseEvent("mouseMoveEvent", event); } function mousePressEvent(event) { printMouseEvent("mousePressEvent", event); } - function mouseReleaseEvent(event) { printMouseEvent("mouseReleaseEvent", event); } - Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); - function printTouchEvent(eventName, event) { print(eventName); @@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) { print(" event.radius=" + event.radius); print(" event.isPinching=" + event.isPinching); print(" event.isPinchOpening=" + event.isPinchOpening); - print(" event.angle=" + event.angle); for (var i = 0; i < event.points.length; i++) { print(" event.angles[" + i + "]:" + event.angles[i]); @@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) { print(" event.isRotating=" + event.isRotating); print(" event.rotating=" + event.rotating); } - function touchBeginEvent(event) { printTouchEvent("touchBeginEvent", event); } - function touchUpdateEvent(event) { printTouchEvent("touchUpdateEvent", event); } - function touchEndEvent(event) { printTouchEvent("touchEndEvent", event); } @@ -167,8 +143,6 @@ function touchEndEvent(event) { Controller.touchBeginEvent.connect(touchBeginEvent); Controller.touchUpdateEvent.connect(touchUpdateEvent); Controller.touchEndEvent.connect(touchEndEvent); - - function wheelEvent(event) { print("wheelEvent"); print(" event.x,y=" + event.x + ", " + event.y); @@ -182,9 +156,7 @@ function wheelEvent(event) { print(" event.isMeta=" + event.isMeta); print(" event.isAlt=" + event.isAlt); } - Controller.wheelEvent.connect(wheelEvent); - function scriptEnding() { // re-enabled the standard application for touch events Controller.releaseKeyEvents({ text: "A" }); @@ -194,5 +166,4 @@ function scriptEnding() { Controller.releaseKeyEvents({ text: "page up" }); Controller.releaseKeyEvents({ text: "page down" }); } - -Script.scriptEnding.connect(scriptEnding); +Script.scriptEnding.connect(scriptEnding); \ No newline at end of file From ae2f7f6ff687ab86f70a8fc8a77cefb29c9e9f04 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 22 Oct 2015 18:50:23 -0700 Subject: [PATCH 121/232] Fix paddleBall.js --- examples/controllers/hydra/paddleBall.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 13c6e2eb62..91b2520e1d 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -32,14 +32,14 @@ var SPRING_FORCE = 15.0; var lastSoundTime = 0; var gameOn = false; var leftHanded = true; -var controllerID; +var hand; function setControllerID() { if (leftHanded) { - controllerID = 1; + hand = Controller.Standard.LeftHand; } else { - controllerID = 3; + hand = Controller.Standard.RightHand; } } @@ -63,7 +63,7 @@ var ball, paddle, paddleModel, line; function createEntities() { ball = Entities.addEntity( { type: "Sphere", - position: Controller.getSpatialControlPosition(controllerID), + position: Controller.getPoseValue(hand).translation, dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, color: BALL_COLOR, gravity: { x: 0, y: GRAVITY, z: 0 }, @@ -73,27 +73,27 @@ function createEntities() { paddle = Entities.addEntity( { type: "Box", - position: Controller.getSpatialControlPosition(controllerID), + position: Controller.getPoseValue(hand).translation, dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, damping: 0.10, visible: false, - rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, collisionsWillMove: false }); modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; paddleModel = Entities.addEntity( { type: "Model", - position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET), + position: Vec3.sum(Controller.getPoseValue(hand).translation, PADDLE_BOX_OFFSET), dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: true, modelURL: modelURL, damping: 0.10, - rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), + rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, collisionsWillMove: false }); line = Overlays.addOverlay("line3d", { @@ -118,7 +118,7 @@ function deleteEntities() { } function update(deltaTime) { - var palmPosition = Controller.getSpatialControlPosition(controllerID); + var palmPosition = Controller.getPoseValue(hand).translation; var controllerActive = (Vec3.length(palmPosition) > 0); if (!gameOn && controllerActive) { @@ -133,7 +133,7 @@ function update(deltaTime) { } var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); - var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation); + var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation), paddleOrientation); var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); @@ -146,10 +146,10 @@ function update(deltaTime) { Entities.editEntity(ball, { velocity: ballVelocity }); Overlays.editOverlay(line, { start: props.position, end: holdPosition }); Entities.editEntity(paddle, { position: holdPosition, - velocity: Controller.getSpatialControlVelocity(controllerID), + velocity: Controller.getPoseValue(hand).velocity, rotation: paddleWorldOrientation }); Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), - velocity: Controller.getSpatialControlVelocity(controllerID), + velocity: Controller.getPoseValue(hand).velocity, rotation: paddleWorldOrientation }); } From 91804fbc04a2d586e2f80dad4d80abf116696dfa Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 22:02:09 -0700 Subject: [PATCH 122/232] Fix ordering of standard vs device routes --- .../src/controllers/UserInputMapper.cpp | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 2ed81aecba..8cd6618bfc 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "StandardController.h" #include "Logging.h" @@ -653,7 +654,16 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { return Input(STANDARD_DEVICE, pose, ChannelType::POSE); } +static auto lastDebugTime = usecTimestampNow(); +static auto debugRoutes = false; +static const auto DEBUG_INTERVAL = USECS_PER_SECOND; + void UserInputMapper::runMappings() { + auto now = usecTimestampNow(); + if (now - lastDebugTime > DEBUG_INTERVAL) { + lastDebugTime = now; + debugRoutes = true; + } static auto deviceNames = getDeviceNames(); for (auto endpointEntry : this->_endpointsByInput) { endpointEntry.second->reset(); @@ -673,17 +683,17 @@ void UserInputMapper::runMappings() { } applyRoute(route); } + debugRoutes = false; } - void UserInputMapper::applyRoute(const Route::Pointer& route) { - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Applying route " << route->json; } if (route->conditional) { if (!route->conditional->satisfied()) { - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Conditional failed"; } return; @@ -698,7 +708,7 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { // I press the button. The exception is if I'm wiring a control back to itself // in order to adjust my interface, like inverting the Y axis on an analog stick if (!source->readable()) { - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Source unreadable"; } return; @@ -708,14 +718,14 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { // THis could happen if the route destination failed to create // FIXME: Maybe do not create the route if the destination failed and avoid this case ? if (!destination) { - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Bad Destination"; } return; } if (!destination->writeable()) { - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Destination unwritable"; } return; @@ -723,17 +733,24 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { // Fetch the value, may have been overriden by previous loopback routes if (source->isPose()) { - if (route->debug) { - qCDebug(controllers) << "Applying pose"; - } Pose value = getPose(source); + static const Pose IDENTITY_POSE { vec3(), quat() }; + if (debugRoutes && route->debug) { + if (!value.valid) { + qCDebug(controllers) << "Applying invalid pose"; + } else if (value == IDENTITY_POSE) { + qCDebug(controllers) << "Applying identity pose"; + } else { + qCDebug(controllers) << "Applying valid pose"; + } + } // no filters yet for pose destination->apply(value, Pose(), source); } else { // Fetch the value, may have been overriden by previous loopback routes float value = getValue(source); - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Value was " << value; } // Apply each of the filters. @@ -741,7 +758,7 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { value = filter->apply(value); } - if (route->debug) { + if (debugRoutes && route->debug) { qCDebug(controllers) << "Filtered value was " << value; } @@ -1148,13 +1165,13 @@ void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { // because standard -> action is the first set of routes added. Route::List standardRoutes = mapping->routes; standardRoutes.remove_if([](const Route::Pointer& value) { - return (value->source->getInput().device == STANDARD_DEVICE); + return (value->source->getInput().device != STANDARD_DEVICE); }); _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end()); Route::List deviceRoutes = mapping->routes; deviceRoutes.remove_if([](const Route::Pointer& value) { - return (value->source->getInput().device != STANDARD_DEVICE); + return (value->source->getInput().device == STANDARD_DEVICE); }); _deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end()); } From 63df9fb959b78bf1a34d5b7bb5f042acfaa5b2de Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 22 Oct 2015 22:37:18 -0700 Subject: [PATCH 123/232] Fixing raw sound playback and air guitar --- examples/controllers/hydra/airGuitar.js | 37 +++++++++++++-------- libraries/audio/src/Sound.cpp | 43 ++++++++++--------------- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/examples/controllers/hydra/airGuitar.js b/examples/controllers/hydra/airGuitar.js index b286cc6084..f8606808c1 100644 --- a/examples/controllers/hydra/airGuitar.js +++ b/examples/controllers/hydra/airGuitar.js @@ -90,7 +90,7 @@ var audioInjector = null; var selectorPressed = false; var position; -MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0); +MyAvatar.attach(guitarModel, "Hips", {x: leftHanded ? -0.2 : 0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, leftHanded ? 75 : -75), 1.0); function checkHands(deltaTime) { var strumVelocity = Controller.getPoseValue(strumHand).velocity; @@ -114,27 +114,32 @@ function checkHands(deltaTime) { // Change guitars if button FWD (5) pressed if (Controller.getValue(changeGuitar)) { - print("changeGuitar:" + changeGuitar); if (!selectorPressed) { + print("changeGuitar:" + changeGuitar); guitarSelector += NUM_CHORDS; if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { guitarSelector = 0; } + print("new guitarBase: " + guitarSelector); + stopAudio(true); selectorPressed = true; } } else { selectorPressed = false; } - //print("selectorPressed:" + selectorPressed); if (Controller.getValue(chord1)) { whichChord = 1; + stopAudio(true); } else if (Controller.getValue(chord2)) { whichChord = 2; + stopAudio(true); } else if (Controller.getValue(chord3)) { whichChord = 3; + stopAudio(true); } else if (Controller.getValue(chord4)) { whichChord = 4; + stopAudio(true); } var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; @@ -154,26 +159,27 @@ function checkHands(deltaTime) { lastPosition = strumHandPosition; } -function playChord(position, volume) { +function stopAudio(killInjector) { if (audioInjector && audioInjector.isPlaying) { print("stopped sound"); audioInjector.stop(); } - + if (killInjector) { + audioInjector = null; + } +} + + +function playChord(position, volume) { + stopAudio(); print("Played sound: " + whichChord + " at volume " + volume); if (!audioInjector) { - - // FIXME - we apparenlty broke RAW file playback, so we need WAV files for all these chords. In the mean - // time, we will just play the heyMan wave file for all chords - var chord = heyManWave; // chords[guitarSelector + whichChord]; - + var index = guitarSelector + whichChord; + var chord = chords[guitarSelector + whichChord]; audioInjector = Audio.playSound(chord, { position: position, volume: volume }); - print("audioInjector: " + JSON.stringify(audioInjector)); } else { - print("audioInjector: " + JSON.stringify(audioInjector)); audioInjector.restart(); } - } function keyPressEvent(event) { @@ -181,15 +187,19 @@ function keyPressEvent(event) { keyVolume = 0.4; if (event.text == "1") { whichChord = 1; + stopAudio(true); playChord(MyAvatar.position, keyVolume); } else if (event.text == "2") { whichChord = 2; + stopAudio(true); playChord(MyAvatar.position, keyVolume); } else if (event.text == "3") { whichChord = 3; + stopAudio(true); playChord(MyAvatar.position, keyVolume); } else if (event.text == "4") { whichChord = 4; + stopAudio(true); playChord(MyAvatar.position, keyVolume); } } @@ -197,6 +207,7 @@ function keyPressEvent(event) { function scriptEnding() { MyAvatar.detachOne(guitarModel); } + // Connect a call back that happens every frame Script.update.connect(checkHands); Script.scriptEnding.connect(scriptEnding); diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 2457bda74a..2ce2c47fef 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -59,39 +59,30 @@ Sound::Sound(const QUrl& url, bool isStereo) : void Sound::downloadFinished(const QByteArray& data) { // replace our byte array with the downloaded data QByteArray rawAudioByteArray = QByteArray(data); - QString fileName = getURL().fileName(); - - const QString WAV_EXTENSION = ".wav"; + QString fileName = getURL().fileName().toLower(); + static const QString WAV_EXTENSION = ".wav"; + static const QString RAW_EXTENSION = ".raw"; if (fileName.endsWith(WAV_EXTENSION)) { - QString headerContentType = "audio/x-wav"; - //QByteArray headerContentType = reply->rawHeader("Content-Type"); + QByteArray outputAudioByteArray; - // WAV audio file encountered - if (headerContentType == "audio/x-wav" - || headerContentType == "audio/wav" - || headerContentType == "audio/wave" - || fileName.endsWith(WAV_EXTENSION)) { - - QByteArray outputAudioByteArray; - - interpretAsWav(rawAudioByteArray, outputAudioByteArray); - downSample(outputAudioByteArray); - } else { - // check if this was a stereo raw file - // since it's raw the only way for us to know that is if the file was called .stereo.raw - if (fileName.toLower().endsWith("stereo.raw")) { - _isStereo = true; - qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; - } - - // Process as RAW file - downSample(rawAudioByteArray); + interpretAsWav(rawAudioByteArray, outputAudioByteArray); + downSample(outputAudioByteArray); + trimFrames(); + } else if (fileName.endsWith(RAW_EXTENSION)) { + // check if this was a stereo raw file + // since it's raw the only way for us to know that is if the file was called .stereo.raw + if (fileName.toLower().endsWith("stereo.raw")) { + _isStereo = true; + qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; } + + // Process as RAW file + downSample(rawAudioByteArray); trimFrames(); } else { - qCDebug(audio) << "Network reply without 'Content-Type'."; + qCDebug(audio) << "Unknown sound file type"; } _isReady = true; From 71dfff7c3579c9395c03c1d356cd6535c011660b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 12:00:40 -0700 Subject: [PATCH 124/232] first cut at adding MyAvatar.xxxHandPose --- interface/src/avatar/MyAvatar.cpp | 28 ++++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 20 ++++++++++++++++---- libraries/avatars/src/HandData.h | 1 + 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d822d37055..6e08ca24cf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -596,6 +596,34 @@ glm::vec3 MyAvatar::getRightHandTipPosition() const { return palmData ? palmData->getTipPosition() : glm::vec3(0.0f); } +controller::Pose MyAvatar::getLeftHandPose() const { + const int LEFT_HAND = 0; + auto palmData = getActivePalm(LEFT_HAND); + return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), + palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); +} + +controller::Pose MyAvatar::getRightHandPose() const { + const int RIGHT_HAND = 1; + auto palmData = getActivePalm(RIGHT_HAND); + return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), + palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); +} + +controller::Pose MyAvatar::getLeftHandTipPose() const { + const int LEFT_HAND = 0; + auto palmData = getActivePalm(LEFT_HAND); + return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), + palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); +} + +controller::Pose MyAvatar::getRightHandTipPose() const { + const int RIGHT_HAND = 1; + auto palmData = getActivePalm(RIGHT_HAND); + return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), + palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); +} + // virtual void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { // don't render if we've been asked to disable local rendering diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c80a855149..0a3d7dedf4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -16,6 +16,8 @@ #include #include +#include + #include "Avatar.h" #include "AtRestDetector.h" @@ -64,10 +66,15 @@ class MyAvatar : public Avatar { //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) - Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) - Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition) - Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition) - Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition) + Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) + Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition) + Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition) + Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition) + + Q_PROPERTY(controller::Pose leftHandPose READ getLeftHandPose) + Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose) + Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose) + Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose) public: MyAvatar(RigPointer rig); @@ -160,6 +167,11 @@ public: Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const; Q_INVOKABLE glm::vec3 getRightHandTipPosition() const; + Q_INVOKABLE controller::Pose getLeftHandPose() const; + Q_INVOKABLE controller::Pose getRightHandPose() const; + Q_INVOKABLE controller::Pose getLeftHandTipPose() const; + Q_INVOKABLE controller::Pose getRightHandTipPose() const; + AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } void updateLookAtTargetAvatar(); void clearLookAtTargetAvatar(); diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 7514e38055..7cc9faad3d 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -103,6 +103,7 @@ public: const glm::vec3& getRawVelocity() const { return _rawVelocity; } void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } + glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(); } // FIXME void addToPosition(const glm::vec3& delta); void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } From 4a58eeb810bdee721e44842477b15c5eb832e30c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 12:14:33 -0700 Subject: [PATCH 125/232] expose deltaRotation as part of MyAvatar.xxxHandPose --- interface/src/Application.cpp | 2 ++ interface/src/avatar/MyAvatar.cpp | 8 ++++---- libraries/avatars/src/HandData.h | 6 +++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0a634425bc..dd04b89b66 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4851,8 +4851,10 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de angularVelocity = glm::normalize(glm::axis(deltaRotation)); angularVelocity *= (rotationAngle / deltaTime); palm->setRawAngularVelocity(angularVelocity); + palm->setRawDeltaRotation(deltaRotation); // FIXME - do we really want both RawAngularVelocity and RawDeltaRotation } else { palm->setRawAngularVelocity(glm::vec3(0.0f)); + palm->setRawDeltaRotation(glm::quat()); } if (controller::InputDevice::getLowVelocityFilter()) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6e08ca24cf..09f702eb83 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -600,28 +600,28 @@ controller::Pose MyAvatar::getLeftHandPose() const { const int LEFT_HAND = 0; auto palmData = getActivePalm(LEFT_HAND); return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), - palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData->getVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandPose() const { const int RIGHT_HAND = 1; auto palmData = getActivePalm(RIGHT_HAND); return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), - palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData->getVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); } controller::Pose MyAvatar::getLeftHandTipPose() const { const int LEFT_HAND = 0; auto palmData = getActivePalm(LEFT_HAND); return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), - palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData->getTipVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandTipPose() const { const int RIGHT_HAND = 1; auto palmData = getActivePalm(RIGHT_HAND); return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), - palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + palmData->getTipVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); } // virtual diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 7cc9faad3d..a194620f3a 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -101,9 +101,12 @@ public: void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; } const glm::vec3& getRawVelocity() const { return _rawVelocity; } + void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } - glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(); } // FIXME + void setRawDeltaRotation(glm::quat rawDeltaRotation) { _rawDeltaRotation = rawDeltaRotation; } // FIXME, is this really what we want? + glm::quat getRawDeltaRotation() const { return _rawDeltaRotation; } + void addToPosition(const glm::vec3& delta); void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } @@ -156,6 +159,7 @@ private: glm::vec3 _rawPosition; glm::vec3 _rawVelocity; glm::vec3 _rawAngularVelocity; + glm::quat _rawDeltaRotation; glm::quat _lastRotation; glm::vec3 _tipPosition; From f90844449d33b0c86bb355b2dbd8dffc22290665 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 13:16:05 -0700 Subject: [PATCH 126/232] fix angular velocity --- interface/src/Application.cpp | 2 -- interface/src/avatar/MyAvatar.cpp | 8 ++++---- libraries/avatars/src/HandData.h | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dd04b89b66..0a634425bc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4851,10 +4851,8 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de angularVelocity = glm::normalize(glm::axis(deltaRotation)); angularVelocity *= (rotationAngle / deltaTime); palm->setRawAngularVelocity(angularVelocity); - palm->setRawDeltaRotation(deltaRotation); // FIXME - do we really want both RawAngularVelocity and RawDeltaRotation } else { palm->setRawAngularVelocity(glm::vec3(0.0f)); - palm->setRawDeltaRotation(glm::quat()); } if (controller::InputDevice::getLowVelocityFilter()) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 09f702eb83..6e08ca24cf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -600,28 +600,28 @@ controller::Pose MyAvatar::getLeftHandPose() const { const int LEFT_HAND = 0; auto palmData = getActivePalm(LEFT_HAND); return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), - palmData->getVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); + palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandPose() const { const int RIGHT_HAND = 1; auto palmData = getActivePalm(RIGHT_HAND); return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), - palmData->getVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); + palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getLeftHandTipPose() const { const int LEFT_HAND = 0; auto palmData = getActivePalm(LEFT_HAND); return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), - palmData->getTipVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); + palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandTipPose() const { const int RIGHT_HAND = 1; auto palmData = getActivePalm(RIGHT_HAND); return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), - palmData->getTipVelocity(), palmData->getRawDeltaRotation()) : controller::Pose(); + palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); } // virtual diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index a194620f3a..855da63870 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -104,8 +104,7 @@ public: void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } - void setRawDeltaRotation(glm::quat rawDeltaRotation) { _rawDeltaRotation = rawDeltaRotation; } // FIXME, is this really what we want? - glm::quat getRawDeltaRotation() const { return _rawDeltaRotation; } + glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); } void addToPosition(const glm::vec3& delta); From e8be92cab837ec32764134663261440af0d73d7a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 09:18:44 -0700 Subject: [PATCH 127/232] 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 128/232] 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 129/232] 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 130/232] 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 131/232] 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 6001968d92732671eb80504bece7de2857d2f5f0 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 23 Oct 2015 13:52:22 -0700 Subject: [PATCH 132/232] Fix paddleBall.js --- examples/controllers/hydra/paddleBall.js | 32 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 91b2520e1d..87e81b6801 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -37,9 +37,17 @@ var hand; function setControllerID() { if (leftHanded) { - hand = Controller.Standard.LeftHand; + hand = MyAvatar.getLeftHandPose(); } else { - hand = Controller.Standard.RightHand; + hand = MyAvatar.rightHandPosition; + } +} + +function getHandPose(){ + if (leftHanded) { + return MyAvatar.getLeftHandPose(); + } else { + return MyAvatar.rightHandPosition; } } @@ -63,7 +71,7 @@ var ball, paddle, paddleModel, line; function createEntities() { ball = Entities.addEntity( { type: "Sphere", - position: Controller.getPoseValue(hand).translation, + position: getHandPose().translation, dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, color: BALL_COLOR, gravity: { x: 0, y: GRAVITY, z: 0 }, @@ -73,27 +81,29 @@ function createEntities() { paddle = Entities.addEntity( { type: "Box", - position: Controller.getPoseValue(hand).translation, + position: getHandPose().translation, dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, damping: 0.10, visible: false, - rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, + //rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, + rotation : getHandPose().rotation, collisionsWillMove: false }); modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; paddleModel = Entities.addEntity( { type: "Model", - position: Vec3.sum(Controller.getPoseValue(hand).translation, PADDLE_BOX_OFFSET), + position: Vec3.sum(hand, PADDLE_BOX_OFFSET), dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: true, modelURL: modelURL, damping: 0.10, - rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, + //rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, + rotation : getHandPose().rotation, collisionsWillMove: false }); line = Overlays.addOverlay("line3d", { @@ -118,7 +128,7 @@ function deleteEntities() { } function update(deltaTime) { - var palmPosition = Controller.getPoseValue(hand).translation; + var palmPosition = getHandPose().translation; var controllerActive = (Vec3.length(palmPosition) > 0); if (!gameOn && controllerActive) { @@ -133,7 +143,7 @@ function update(deltaTime) { } var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); - var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation), paddleOrientation); + var paddleWorldOrientation = Quat.multiply(getHandPose().rotation, paddleOrientation); var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); @@ -146,10 +156,10 @@ function update(deltaTime) { Entities.editEntity(ball, { velocity: ballVelocity }); Overlays.editOverlay(line, { start: props.position, end: holdPosition }); Entities.editEntity(paddle, { position: holdPosition, - velocity: Controller.getPoseValue(hand).velocity, + velocity: getHandPose().velocity, //fix this maybe rotation: paddleWorldOrientation }); Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), - velocity: Controller.getPoseValue(hand).velocity, + velocity: getHandPose().velocity, //fix this maybe rotation: paddleWorldOrientation }); } From 7e3192d0f6d4e15baafc74e1205bb06f2251d8b0 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 14:04:06 -0700 Subject: [PATCH 133/232] 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]; From f14a321d035ae778992d826f5b9b6c1d15b657f9 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 23 Oct 2015 15:03:04 -0700 Subject: [PATCH 134/232] Adding a amoving Average for the velocity of the hydra --- .../src/input-plugins/SixenseManager.cpp | 21 +++++++++++-- .../src/input-plugins/SixenseManager.h | 31 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 29e60ca5ec..1f72d66019 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -178,6 +178,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { if (sixenseGetNumActiveControllers() == 0) { _poseStateMap.clear(); + _collectedSamples.clear(); return; } @@ -234,9 +235,12 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { } else { _poseStateMap.clear(); + _collectedSamples.clear(); } } else { - _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND] = controller::Pose(); + auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; + _poseStateMap[hand] = controller::Pose(); + _collectedSamples[hand].clear(); } } @@ -387,6 +391,8 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { #ifdef HAVE_SIXENSE + auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; + // From ABOVE the sixense coordinate frame looks like this: // // | @@ -401,7 +407,7 @@ void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::q // | // | // z - auto prevPose = _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND]; + auto prevPose = _poseStateMap[hand]; // Transform the measured position into body frame. position = _avatarRotation * (position + _avatarPosition); @@ -448,7 +454,10 @@ void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::q glm::vec3 velocity(0.0f); glm::quat angularVelocity; + + if (prevPose.isValid() && deltaTime > std::numeric_limits::epsilon()) { + velocity = (position - prevPose.getTranslation()) / deltaTime; auto deltaRot = rotation * glm::conjugate(prevPose.getRotation()); @@ -456,9 +465,15 @@ void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::q auto angle = glm::angle(deltaRot); angularVelocity = glm::angleAxis(angle / deltaTime, axis); + // Average + auto& samples = _collectedSamples[hand]; + samples.addSample(velocity); + velocity = samples.average; + } else if (!prevPose.isValid()) { + _collectedSamples[hand].clear(); } - _poseStateMap[left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND] = controller::Pose(position, rotation, velocity, angularVelocity); + _poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity); #endif // HAVE_SIXENSE } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 368321e669..c0d908ed45 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -94,6 +94,36 @@ private: glm::vec3 _reachRight; float _lastDistance; bool _useSixenseFilter = true; + + template class MovingAverage { + public: + using Samples = std::list< T >; + Samples samples; + T average; + + void clear() { + samples.clear(); + } + + bool isAverageValid() const { return !samples.empty(); } + + void addSample(T sample) { + samples.push_front(sample); + int numSamples = samples.size(); + + if (numSamples < MAX_NUM_SAMPLES) { + average = (sample + average * float(numSamples - 1)) / float(numSamples); + } else { + T tail = samples.back(); + samples.pop_back(); + average = average + (sample - tail) / float(numSamples); + } + } + }; + + static const int MAX_NUM_AVERAGING_SAMPLES = 10; // At ~100 updates per seconds this means averaging over ~.1s + using MovingAverageMap = std::map< int, MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES> >; + MovingAverageMap _collectedSamples; #ifdef __APPLE__ QLibrary* _sixenseLibrary; @@ -109,3 +139,4 @@ private: }; #endif // hifi_SixenseManager_h + From 37e1e43ce0b573ce77d08a38be06c363ab6d5253 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 23 Oct 2015 15:48:29 -0700 Subject: [PATCH 135/232] Fix paddleBall.js --- examples/controllers/hydra/paddleBall.js | 49 +++++++----------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/examples/controllers/hydra/paddleBall.js b/examples/controllers/hydra/paddleBall.js index 87e81b6801..d90a78c260 100644 --- a/examples/controllers/hydra/paddleBall.js +++ b/examples/controllers/hydra/paddleBall.js @@ -20,10 +20,11 @@ var BALL_SIZE = 0.08; var PADDLE_SIZE = 0.20; var PADDLE_THICKNESS = 0.06; var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; -var BALL_COLOR = { red: 255, green: 0, blue: 0 }; +var BALL_COLOR = { red: 0, green: 255, blue: 0 }; var LINE_COLOR = { red: 255, green: 255, blue: 0 }; var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; +//probably we need to fix these initial values (offsets and orientation) var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); @@ -32,26 +33,7 @@ var SPRING_FORCE = 15.0; var lastSoundTime = 0; var gameOn = false; var leftHanded = true; -var hand; - -function setControllerID() { - if (leftHanded) { - hand = MyAvatar.getLeftHandPose(); - } else { - hand = MyAvatar.rightHandPosition; - } -} - -function getHandPose(){ - if (leftHanded) { - return MyAvatar.getLeftHandPose(); - } else { - return MyAvatar.rightHandPosition; - } -} - -setControllerID(); Menu.addMenu("PaddleBall"); Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true }); @@ -71,7 +53,7 @@ var ball, paddle, paddleModel, line; function createEntities() { ball = Entities.addEntity( { type: "Sphere", - position: getHandPose().translation, + position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, color: BALL_COLOR, gravity: { x: 0, y: GRAVITY, z: 0 }, @@ -81,30 +63,28 @@ function createEntities() { paddle = Entities.addEntity( { type: "Box", - position: getHandPose().translation, + position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, damping: 0.10, visible: false, - //rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, - rotation : getHandPose().rotation, - collisionsWillMove: false }); + rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, + collisionsWillMove: false }); modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; paddleModel = Entities.addEntity( { type: "Model", - position: Vec3.sum(hand, PADDLE_BOX_OFFSET), + position: Vec3.sum( leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, PADDLE_BOX_OFFSET), dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, color: PADDLE_COLOR, gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: true, modelURL: modelURL, damping: 0.10, - //rotation: Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(hand).rotation, - rotation : getHandPose().rotation, - collisionsWillMove: false }); + rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, + collisionsWillMove: false }); line = Overlays.addOverlay("line3d", { start: { x: 0, y: 0, z: 0 }, @@ -128,7 +108,7 @@ function deleteEntities() { } function update(deltaTime) { - var palmPosition = getHandPose().translation; + var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; var controllerActive = (Vec3.length(palmPosition) > 0); if (!gameOn && controllerActive) { @@ -143,8 +123,8 @@ function update(deltaTime) { } var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); - var paddleWorldOrientation = Quat.multiply(getHandPose().rotation, paddleOrientation); - var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), + var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation); + var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); var props = Entities.getEntityProperties(ball); @@ -156,10 +136,10 @@ function update(deltaTime) { Entities.editEntity(ball, { velocity: ballVelocity }); Overlays.editOverlay(line, { start: props.position, end: holdPosition }); Entities.editEntity(paddle, { position: holdPosition, - velocity: getHandPose().velocity, //fix this maybe + velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, rotation: paddleWorldOrientation }); Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), - velocity: getHandPose().velocity, //fix this maybe + velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, rotation: paddleWorldOrientation }); } @@ -192,7 +172,6 @@ function menuItemEvent(menuItem) { leftHanded = Menu.isOptionChecked("Left-Handed"); } if ((leftHanded != oldHanded) && gameOn) { - setControllerID(); deleteEntities(); createEntities(); } From d5a90e273e418d02cfa0f69c8ee3687753df6b53 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 15:56:55 -0700 Subject: [PATCH 136/232] fix AnyEndpoint support from JS --- .../src/controllers/UserInputMapper.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index e924718fca..30fd650eba 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -217,10 +217,10 @@ public: } virtual float value() override { - float result = 0; + float result = 0.0f; for (auto& child : _children) { float childResult = child->value(); - if (childResult != 0.0f) { + if (childResult != 0.0f && result == 0.0f) { result = childResult; } } @@ -856,6 +856,21 @@ Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { return result; } + if (endpoint.isArray()) { + int length = endpoint.property("length").toInteger(); + Endpoint::List children; + for (int i = 0; i < length; i++) { + QScriptValue arrayItem = endpoint.property(i); + Endpoint::Pointer destination = endpointFor(arrayItem); + if (!destination) { + return Endpoint::Pointer(); + } + children.push_back(destination); + } + return std::make_shared(children); + } + + qWarning() << "Unsupported input type " << endpoint.toString(); return Endpoint::Pointer(); } From 5c09391170564a0b954de0fea98d46cb8c33a543 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 23 Oct 2015 16:19:40 -0700 Subject: [PATCH 137/232] fix toyball.js --- examples/controllers/hydra/toyball.js | 64 +++++++++------------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/examples/controllers/hydra/toyball.js b/examples/controllers/hydra/toyball.js index 0f5db9b2c0..b2616af3e2 100644 --- a/examples/controllers/hydra/toyball.js +++ b/examples/controllers/hydra/toyball.js @@ -20,13 +20,8 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; // maybe we should make these constants... var LEFT_PALM = 0; var LEFT_TIP = 1; -var LEFT_BUTTON_FWD = 5; -var LEFT_BUTTON_3 = 3; - var RIGHT_PALM = 2; var RIGHT_TIP = 3; -var RIGHT_BUTTON_FWD = 11; -var RIGHT_BUTTON_3 = 9; var BALL_RADIUS = 0.08; var GRAVITY_STRENGTH = 3.0; @@ -69,9 +64,6 @@ function getBallHoldPosition(whichSide) { } function checkControllerSide(whichSide) { - var BUTTON_FWD; - var BUTTON_3; - var TRIGGER; var palmPosition; var palmRotation; var ballAlreadyInHand; @@ -79,35 +71,35 @@ function checkControllerSide(whichSide) { var linearVelocity; var angularVelocity; var AVERAGE_FACTOR = 0.33; - + var grabButtonPressed; + if (whichSide == LEFT_PALM) { - BUTTON_FWD = LEFT_BUTTON_FWD; - BUTTON_3 = LEFT_BUTTON_3; - TRIGGER = 0; - palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); - palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM)); + palmPosition = MyAvatar.leftHandPose.translation; + palmRotation = MyAvatar.leftHandPose.rotation; ballAlreadyInHand = leftBallAlreadyInHand; handMessage = "LEFT"; - averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(LEFT_TIP)), + averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity), Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0])); - linearVelocity = averageLinearVelocity[0]; - angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(LEFT_TIP)); + + linearVelocity = averageLinearVelocity[0]; + angularVelocity = MyAvatar.leftHandTipPose.angularVelocity; + grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5); + } else { - BUTTON_FWD = RIGHT_BUTTON_FWD; - BUTTON_3 = RIGHT_BUTTON_3; - TRIGGER = 1; - palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); - palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM)); - ballAlreadyInHand = rightBallAlreadyInHand; - averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(RIGHT_TIP)), + palmPosition = MyAvatar.rightHandPose.translation; + palmRotation = MyAvatar.rightHandPose.rotation; + ballAlreadyInHand = rightBallAlreadyInHand; + averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity), Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1])); - linearVelocity = averageLinearVelocity[1]; - angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP)); + + linearVelocity = averageLinearVelocity[1]; + angularVelocity = MyAvatar.rightHandTipPose.angularVelocity; handMessage = "RIGHT"; + grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5); + } - var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5)); - + // If I don't currently have a ball in my hand, then try to catch closest one if (!ballAlreadyInHand && grabButtonPressed) { var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius); @@ -231,22 +223,10 @@ function checkControllerSide(whichSide) { } } } - - function checkController(deltaTime) { - var numberOfButtons = Controller.getNumberOfButtons(); - var numberOfTriggers = Controller.getNumberOfTriggers(); - var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); - var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; - // this is expected for hydras - if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { - debugPrint("total buttons = " + numberOfButtons + ", Triggers = " + numberOfTriggers + ", controllers/trigger = " + controllersPerTrigger); - return; // bail if no hydra - } - - checkControllerSide(LEFT_PALM); - checkControllerSide(RIGHT_PALM); + checkControllerSide(LEFT_PALM); + checkControllerSide(RIGHT_PALM); } From 6b795364c8ed52a2ee2a82d557906710066c86d7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 16:30:40 -0700 Subject: [PATCH 138/232] make handGrab treat shoulder bumbers and triggers as merged control --- examples/controllers/handControllerGrab.js | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 17d16d1718..80a3ea98a4 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -148,6 +148,7 @@ function MyController(hand, triggerAction) { this.state = STATE_OFF; this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value + this.rawTriggerValue = 0; var _this = this; @@ -244,12 +245,18 @@ function MyController(hand, triggerAction) { this.pointer = null; }; - this.updateSmoothedTrigger = function() { - var triggerValue = Controller.getValue(this.triggerAction); + this.eitherTrigger = function (value) { + _this.rawTriggerValue = value; + }; + + this.updateSmoothedTrigger = function () { + //var triggerValue = Controller.getValue(this.triggerAction); // this.rawTriggerValue; // + var triggerValue = this.rawTriggerValue; // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - } + + }; this.triggerSmoothedSqueezed = function() { return this.triggerValue > TRIGGER_ON_VALUE; @@ -259,8 +266,9 @@ function MyController(hand, triggerAction) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { - var triggerValue = Controller.getValue(this.triggerAction); + this.triggerSqueezed = function() { + var triggerValue = Controller.getValue(this.triggerAction); // this.rawTriggerValue; // + print("triggerSqueezed() triggerValue:" + triggerValue); return triggerValue > TRIGGER_ON_VALUE; }; @@ -861,6 +869,12 @@ function MyController(hand, triggerAction) { var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); +var mapping = Controller.newMapping("handGrab"); +mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger); +mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger); +Controller.enableMapping("handGrab"); + + function update() { rightController.update(); leftController.update(); @@ -869,6 +883,7 @@ function update() { function cleanup() { rightController.cleanup(); leftController.cleanup(); + Controller.disableMapping("handGrab"); } Script.scriptEnding.connect(cleanup); From 9285cf8e2cff33e2389a1b0cde10ed85b05cae49 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:12:26 -0700 Subject: [PATCH 139/232] fix hydraGrabHockey.js to use new API --- examples/example/games/hydraGrabHockey.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/example/games/hydraGrabHockey.js b/examples/example/games/hydraGrabHockey.js index b9f760fa08..26f9a443ab 100644 --- a/examples/example/games/hydraGrabHockey.js +++ b/examples/example/games/hydraGrabHockey.js @@ -59,9 +59,7 @@ function controller(side) { this.triggerHeld = false; this.triggerThreshold = 0.9; this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.trigger = side; + this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT; this.originalGravity = { x: 0, y: 0, @@ -150,8 +148,8 @@ function controller(side) { this.updateControllerState = function() { - this.palmPosition = Controller.getSpatialControlPosition(this.palm); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; this.triggerValue = Controller.getTriggerValue(this.trigger); } From c64290b5cd0d7400a9fd9e4024fccc6465d41e8d Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 23 Oct 2015 17:13:29 -0700 Subject: [PATCH 140/232] Removed unused variables --- examples/controllers/hydra/toyball.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/controllers/hydra/toyball.js b/examples/controllers/hydra/toyball.js index b2616af3e2..c3374ffb5f 100644 --- a/examples/controllers/hydra/toyball.js +++ b/examples/controllers/hydra/toyball.js @@ -19,9 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; // maybe we should make these constants... var LEFT_PALM = 0; -var LEFT_TIP = 1; var RIGHT_PALM = 2; -var RIGHT_TIP = 3; var BALL_RADIUS = 0.08; var GRAVITY_STRENGTH = 3.0; @@ -179,10 +177,8 @@ function checkControllerSide(whichSide) { if (ballAlreadyInHand) { if (whichSide == LEFT_PALM) { handEntity = leftHandEntity; - whichTip = LEFT_TIP; } else { handEntity = rightHandEntity; - whichTip = RIGHT_TIP; } // If holding the ball keep it in the palm From ed20e7d209dc12eb73f057ac4b3202e0525f8726 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:14:13 -0700 Subject: [PATCH 141/232] CR feedback --- examples/controllers/handControllerGrab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 80a3ea98a4..2b370a3b89 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -869,10 +869,10 @@ function MyController(hand, triggerAction) { var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); -var mapping = Controller.newMapping("handGrab"); +var mapping = Controller.newMapping("com.highfidelity.handControllerGrab"); mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger); mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger); -Controller.enableMapping("handGrab"); +Controller.enableMapping("com.highfidelity.handControllerGrab"); function update() { From 090dc5409b968356fcd69685ae84b04d3a78047a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:14:59 -0700 Subject: [PATCH 142/232] CR feedback --- examples/controllers/handControllerGrab.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 2b370a3b89..9e21429cdc 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -250,7 +250,6 @@ function MyController(hand, triggerAction) { }; this.updateSmoothedTrigger = function () { - //var triggerValue = Controller.getValue(this.triggerAction); // this.rawTriggerValue; // var triggerValue = this.rawTriggerValue; // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + @@ -267,8 +266,7 @@ function MyController(hand, triggerAction) { }; this.triggerSqueezed = function() { - var triggerValue = Controller.getValue(this.triggerAction); // this.rawTriggerValue; // - print("triggerSqueezed() triggerValue:" + triggerValue); + var triggerValue = this.rawTriggerValue; return triggerValue > TRIGGER_ON_VALUE; }; From 9ba1b800d91655bda082a35035fcad1a138c4dd5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:15:39 -0700 Subject: [PATCH 143/232] CR feedback --- examples/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9e21429cdc..a61f52117b 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -881,7 +881,7 @@ function update() { function cleanup() { rightController.cleanup(); leftController.cleanup(); - Controller.disableMapping("handGrab"); + Controller.disableMapping("com.highfidelity.handControllerGrab"); } Script.scriptEnding.connect(cleanup); From 0fdd32709f6eb7c52c3dc98568823516cb94671b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 13:28:36 -0700 Subject: [PATCH 144/232] Moving conditionals and endpoints out of UserInputMapper Conflicts: libraries/controllers/src/controllers/UserInputMapper.cpp --- .../src/controllers/UserInputMapper.cpp | 341 +----------------- .../controllers/{ => impl}/Conditional.cpp | 0 .../src/controllers/{ => impl}/Conditional.h | 0 .../src/controllers/{ => impl}/Endpoint.cpp | 0 .../src/controllers/{ => impl}/Endpoint.h | 4 +- .../src/controllers/{ => impl}/Filter.cpp | 0 .../src/controllers/{ => impl}/Filter.h | 0 .../src/controllers/{ => impl}/Mapping.cpp | 0 .../src/controllers/{ => impl}/Mapping.h | 0 .../controllers/impl/MappingBuilderProxy.h | 4 +- .../src/controllers/{ => impl}/Route.cpp | 0 .../src/controllers/{ => impl}/Route.h | 0 .../src/controllers/impl/RouteBuilderProxy.h | 8 +- .../impl/conditionals/AndConditional.cpp | 20 + .../impl/conditionals/AndConditional.h | 32 ++ .../impl/conditionals/EndpointConditional.cpp | 9 + .../impl/conditionals/EndpointConditional.h | 27 ++ .../impl/conditionals/NotConditional.cpp | 9 + .../impl/conditionals/NotConditional.h | 16 + .../impl/conditionals/ScriptConditional.cpp | 27 ++ .../impl/conditionals/ScriptConditional.h | 34 ++ .../impl/endpoints/ActionEndpoint.cpp | 40 ++ .../impl/endpoints/ActionEndpoint.h | 41 +++ .../impl/endpoints/AnyEndpoint.cpp | 63 ++++ .../controllers/impl/endpoints/AnyEndpoint.h | 32 ++ .../impl/endpoints/ArrayEndpoint.cpp | 9 + .../impl/endpoints/ArrayEndpoint.h | 43 +++ .../impl/endpoints/CompositeEndpoint.cpp | 32 ++ .../impl/endpoints/CompositeEndpoint.h | 26 ++ .../impl/endpoints/InputEndpoint.cpp | 41 +++ .../impl/endpoints/InputEndpoint.h | 39 ++ .../controllers/impl/endpoints/JSEndpoint.cpp | 9 + .../controllers/impl/endpoints/JSEndpoint.h | 41 +++ .../impl/endpoints/ScriptEndpoint.cpp | 43 +++ .../impl/endpoints/ScriptEndpoint.h | 39 ++ .../impl/endpoints/StandardEndpoint.cpp | 9 + .../impl/endpoints/StandardEndpoint.h | 60 +++ 37 files changed, 766 insertions(+), 332 deletions(-) rename libraries/controllers/src/controllers/{ => impl}/Conditional.cpp (100%) rename libraries/controllers/src/controllers/{ => impl}/Conditional.h (100%) rename libraries/controllers/src/controllers/{ => impl}/Endpoint.cpp (100%) rename libraries/controllers/src/controllers/{ => impl}/Endpoint.h (98%) rename libraries/controllers/src/controllers/{ => impl}/Filter.cpp (100%) rename libraries/controllers/src/controllers/{ => impl}/Filter.h (100%) rename libraries/controllers/src/controllers/{ => impl}/Mapping.cpp (100%) rename libraries/controllers/src/controllers/{ => impl}/Mapping.h (100%) rename libraries/controllers/src/controllers/{ => impl}/Route.cpp (100%) rename libraries/controllers/src/controllers/{ => impl}/Route.h (100%) create mode 100644 libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp create mode 100644 libraries/controllers/src/controllers/impl/conditionals/AndConditional.h create mode 100644 libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp create mode 100644 libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h create mode 100644 libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp create mode 100644 libraries/controllers/src/controllers/impl/conditionals/NotConditional.h create mode 100644 libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp create mode 100644 libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h create mode 100644 libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp create mode 100644 libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 30fd650eba..19a4b78207 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -24,9 +24,22 @@ #include "Logging.h" -#include "Endpoint.h" -#include "Route.h" -#include "Mapping.h" +#include "impl/conditionals/AndConditional.h" +#include "impl/conditionals/EndpointConditional.h" +#include "impl/conditionals/ScriptConditional.h" + +#include "impl/endpoints/ActionEndpoint.h" +#include "impl/endpoints/AnyEndpoint.h" +#include "impl/endpoints/ArrayEndpoint.h" +#include "impl/endpoints/CompositeEndpoint.h" +#include "impl/endpoints/InputEndpoint.h" +#include "impl/endpoints/JSEndpoint.h" +#include "impl/endpoints/ScriptEndpoint.h" +#include "impl/endpoints/StandardEndpoint.h" + +#include "impl/Route.h" +#include "impl/Mapping.h" + namespace controller { const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; @@ -42,300 +55,6 @@ controller::UserInputMapper::UserInputMapper() { namespace controller { -class ScriptEndpoint : public Endpoint { - Q_OBJECT; -public: - ScriptEndpoint(const QScriptValue& callable) - : Endpoint(Input::INVALID_INPUT), _callable(callable) { - } - - virtual float value(); - virtual void apply(float newValue, float oldValue, const Pointer& source); - -protected: - Q_INVOKABLE void updateValue(); - Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); -private: - QScriptValue _callable; - float _lastValue = 0.0f; -}; - -class StandardEndpoint : public VirtualEndpoint { -public: - StandardEndpoint(const Input& input) : VirtualEndpoint(input) {} - virtual bool writeable() const override { return !_written; } - virtual bool readable() const override { return !_read; } - virtual void reset() override { - apply(0.0f, 0.0f, Endpoint::Pointer()); - apply(Pose(), Pose(), Endpoint::Pointer()); - _written = _read = false; - } - - virtual float value() override { - _read = true; - return VirtualEndpoint::value(); - } - - virtual void apply(float newValue, float oldValue, const Pointer& source) override { - // For standard endpoints, the first NON-ZERO write counts. - if (newValue != 0.0) { - _written = true; - } - VirtualEndpoint::apply(newValue, oldValue, source); - } - - virtual Pose pose() override { - _read = true; - return VirtualEndpoint::pose(); - } - - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - if (newValue != Pose()) { - _written = true; - } - VirtualEndpoint::apply(newValue, oldValue, source); - } - -private: - bool _written { false }; - bool _read { false }; -}; - - -class JSEndpoint : public Endpoint { -public: - JSEndpoint(const QJSValue& callable) - : Endpoint(Input::INVALID_INPUT), _callable(callable) { - } - - virtual float value() { - float result = (float)_callable.call().toNumber();; - return result; - } - - virtual void apply(float newValue, float oldValue, const Pointer& source) { - _callable.call(QJSValueList({ QJSValue(newValue) })); - } - -private: - QJSValue _callable; -}; - -float ScriptEndpoint::value() { - updateValue(); - return _lastValue; -} - -void ScriptEndpoint::updateValue() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); - return; - } - - _lastValue = (float)_callable.call().toNumber(); -} - -void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { - internalApply(newValue, oldValue, source->getInput().getID()); -} - -void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, - Q_ARG(float, newValue), - Q_ARG(float, oldValue), - Q_ARG(int, sourceID)); - return; - } - _callable.call(QScriptValue(), - QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); -} - -static const Input INVALID_STANDARD_INPUT = Input(UserInputMapper::STANDARD_DEVICE, Input::INVALID_CHANNEL, ChannelType::INVALID); - -class CompositeEndpoint : public Endpoint, Endpoint::Pair { -public: - CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) - : Endpoint(Input::INVALID_INPUT), Pair(first, second) { - if (first->getInput().device == UserInputMapper::STANDARD_DEVICE && - second->getInput().device == UserInputMapper::STANDARD_DEVICE) { - this->_input = INVALID_STANDARD_INPUT; - } - } - - virtual float value() { - float result = first->value() * -1.0f + second->value(); - return result; - } - - virtual void apply(float newValue, float oldValue, const Pointer& source) { - // Composites are read only - } -}; - -class ArrayEndpoint : public Endpoint { - friend class UserInputMapper; -public: - using Pointer = std::shared_ptr; - ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { } - - virtual float value() override { - return 0.0; - } - - virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override { - for (auto& child : _children) { - if (child->writeable()) { - child->apply(newValue, oldValue, source); - } - } - } - - virtual bool readable() const override { return false; } - -private: - Endpoint::List _children; -}; - -class AnyEndpoint : public Endpoint { - friend class UserInputMapper; -public: - using Pointer = std::shared_ptr; - AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) { - bool standard = true; - // Ensure if we're building a composite of standard devices the composite itself - // is treated as a standard device for rule processing order - for (auto endpoint : children) { - if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) { - standard = false; - break; - } - } - if (standard) { - this->_input = INVALID_STANDARD_INPUT; - } - } - - virtual float value() override { - float result = 0.0f; - for (auto& child : _children) { - float childResult = child->value(); - if (childResult != 0.0f && result == 0.0f) { - result = childResult; - } - } - return result; - } - - virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override { - qFatal("AnyEndpoint is read only"); - } - - // 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) { - if (!child->readable()) { - return false; - } - } - return true; - } - -private: - Endpoint::List _children; -}; - -class InputEndpoint : public Endpoint { -public: - InputEndpoint(const Input& id = Input::INVALID_INPUT) - : Endpoint(id) { - } - - virtual float value() override { - _read = true; - if (isPose()) { - return pose().valid ? 1.0f : 0.0f; - } - auto userInputMapper = DependencyManager::get(); - auto deviceProxy = userInputMapper->getDeviceProxy(_input); - if (!deviceProxy) { - return 0.0f; - } - return deviceProxy->getValue(_input, 0); - } - - // FIXME need support for writing back to vibration / force feedback effects - virtual void apply(float newValue, float oldValue, const Pointer& source) override {} - - virtual Pose pose() override { - _read = true; - if (!isPose()) { - return Pose(); - } - auto userInputMapper = DependencyManager::get(); - auto deviceProxy = userInputMapper->getDeviceProxy(_input); - if (!deviceProxy) { - return Pose(); - } - return deviceProxy->getPose(_input, 0); - } - - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } - - virtual bool writeable() const { return false; } - virtual bool readable() const { return !_read; } - virtual void reset() { _read = false; } - -private: - bool _read { false }; -}; - -class ActionEndpoint : public Endpoint { -public: - ActionEndpoint(const Input& id = Input::INVALID_INPUT) - : Endpoint(id) { - } - - virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { - _currentValue += newValue; - if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); - } - } - - virtual Pose pose() override { return _currentPose; } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - _currentPose = newValue; - if (!_currentPose.isValid()) { - return; - } - if (_input != Input::INVALID_INPUT) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); - } - } - - virtual void reset() override { - _currentValue = 0.0f; - _currentPose = Pose(); - } - -private: - float _currentValue{ 0.0f }; - Pose _currentPose{}; -}; - UserInputMapper::~UserInputMapper() { } @@ -1019,33 +738,6 @@ Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { return result; } -class AndConditional : public Conditional { -public: - using Pointer = std::shared_ptr; - - AndConditional(Conditional::List children) : _children(children) { } - - virtual bool satisfied() override { - for (auto& conditional : _children) { - if (!conditional->satisfied()) { - return false; - } - } - return true; - } - -private: - Conditional::List _children; -}; - -class EndpointConditional : public Conditional { -public: - EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} - virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; } -private: - Endpoint::Pointer _endpoint; -}; - Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { if (value.isArray()) { // Support "when" : [ "GamePad.RB", "GamePad.LB" ] @@ -1290,4 +982,3 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { } -#include "UserInputMapper.moc" diff --git a/libraries/controllers/src/controllers/Conditional.cpp b/libraries/controllers/src/controllers/impl/Conditional.cpp similarity index 100% rename from libraries/controllers/src/controllers/Conditional.cpp rename to libraries/controllers/src/controllers/impl/Conditional.cpp diff --git a/libraries/controllers/src/controllers/Conditional.h b/libraries/controllers/src/controllers/impl/Conditional.h similarity index 100% rename from libraries/controllers/src/controllers/Conditional.h rename to libraries/controllers/src/controllers/impl/Conditional.h diff --git a/libraries/controllers/src/controllers/Endpoint.cpp b/libraries/controllers/src/controllers/impl/Endpoint.cpp similarity index 100% rename from libraries/controllers/src/controllers/Endpoint.cpp rename to libraries/controllers/src/controllers/impl/Endpoint.cpp diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h similarity index 98% rename from libraries/controllers/src/controllers/Endpoint.h rename to libraries/controllers/src/controllers/impl/Endpoint.h index 7a94b06e7e..f5fe058d82 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -16,8 +16,8 @@ #include -#include "Input.h" -#include "Pose.h" +#include "../Input.h" +#include "../Pose.h" class QScriptValue; diff --git a/libraries/controllers/src/controllers/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp similarity index 100% rename from libraries/controllers/src/controllers/Filter.cpp rename to libraries/controllers/src/controllers/impl/Filter.cpp diff --git a/libraries/controllers/src/controllers/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h similarity index 100% rename from libraries/controllers/src/controllers/Filter.h rename to libraries/controllers/src/controllers/impl/Filter.h diff --git a/libraries/controllers/src/controllers/Mapping.cpp b/libraries/controllers/src/controllers/impl/Mapping.cpp similarity index 100% rename from libraries/controllers/src/controllers/Mapping.cpp rename to libraries/controllers/src/controllers/impl/Mapping.cpp diff --git a/libraries/controllers/src/controllers/Mapping.h b/libraries/controllers/src/controllers/impl/Mapping.h similarity index 100% rename from libraries/controllers/src/controllers/Mapping.h rename to libraries/controllers/src/controllers/impl/Mapping.h diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h index 93aa022647..ac9a5a300d 100644 --- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h @@ -12,8 +12,8 @@ #include #include -#include "../Mapping.h" -#include "../Endpoint.h" +#include "Mapping.h" +#include "Endpoint.h" class QJSValue; class QScriptValue; diff --git a/libraries/controllers/src/controllers/Route.cpp b/libraries/controllers/src/controllers/impl/Route.cpp similarity index 100% rename from libraries/controllers/src/controllers/Route.cpp rename to libraries/controllers/src/controllers/impl/Route.cpp diff --git a/libraries/controllers/src/controllers/Route.h b/libraries/controllers/src/controllers/impl/Route.h similarity index 100% rename from libraries/controllers/src/controllers/Route.h rename to libraries/controllers/src/controllers/impl/Route.h diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 0484c5890d..2303f6184f 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -11,10 +11,12 @@ #include -#include "../Filter.h" -#include "../Route.h" -#include "../Mapping.h" +#include "Filter.h" +#include "Route.h" +#include "Mapping.h" + #include "../UserInputMapper.h" + class QJSValue; class QScriptValue; class QJsonValue; diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp new file mode 100644 index 0000000000..1633f9a439 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp @@ -0,0 +1,20 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "AndConditional.h" + +using namespace controller; + +bool AndConditional::satisfied() { + for (auto& conditional : _children) { + if (!conditional->satisfied()) { + return false; + } + } + return true; +} diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h new file mode 100644 index 0000000000..5fc8b7df2a --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h @@ -0,0 +1,32 @@ +// +// 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_AndConditional_h +#define hifi_Controllers_AndConditional_h + +#include "../Conditional.h" + +namespace controller { + +class AndConditional : public Conditional { +public: + using Pointer = std::shared_ptr; + + AndConditional(Conditional::List children) : _children(children) { } + + virtual bool satisfied() override; + +private: + Conditional::List _children; +}; + +} + + +#endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp new file mode 100644 index 0000000000..03e16b8cf9 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "EndpointConditional.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h new file mode 100644 index 0000000000..1e4205afc7 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -0,0 +1,27 @@ +// +// 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_EndpointConditional_h +#define hifi_Controllers_EndpointConditional_h + +#include "../Conditional.h" +#include "../Endpoint.h" + +namespace controller { + +class EndpointConditional : public Conditional { +public: + EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} + virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; } +private: + Endpoint::Pointer _endpoint; +}; + +} +#endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp new file mode 100644 index 0000000000..0c8d602b9e --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "NotConditional.h" diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h new file mode 100644 index 0000000000..3acda07106 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h @@ -0,0 +1,16 @@ +// +// 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_NotConditional_h +#define hifi_Controllers_NotConditional_h + +#include "../Conditional.h" + + +#endif diff --git a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp new file mode 100644 index 0000000000..277b63ed11 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp @@ -0,0 +1,27 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "ScriptConditional.h" + +#include + +using namespace controller; + +bool ScriptConditional::satisfied() { + updateValue(); + return _lastValue; +} + +void ScriptConditional::updateValue() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + return; + } + + _lastValue = _callable.call().toBool(); +} diff --git a/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h new file mode 100644 index 0000000000..800692d02c --- /dev/null +++ b/libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h @@ -0,0 +1,34 @@ +// +// 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_ScriptConditional_h +#define hifi_Controllers_ScriptConditional_h + +#include + +#include + +#include "../Conditional.h" + +namespace controller { + +class ScriptConditional : public QObject, public Conditional { + Q_OBJECT; +public: + ScriptConditional(const QScriptValue& callable) : _callable(callable) { } + virtual bool satisfied() override; +protected: + Q_INVOKABLE void updateValue(); +private: + QScriptValue _callable; + bool _lastValue { false }; +}; + +} +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp new file mode 100644 index 0000000000..d07ef38185 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "ActionEndpoint.h" + +#include + +#include "../../UserInputMapper.h" + +using namespace controller; + +void ActionEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + _currentValue += newValue; + if (_input != Input::INVALID_INPUT) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); + } +} + +void ActionEndpoint::apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) { + _currentPose = newValue; + if (!_currentPose.isValid()) { + return; + } + if (_input != Input::INVALID_INPUT) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); + } +} + +void ActionEndpoint::reset() { + _currentValue = 0.0f; + _currentPose = Pose(); +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h new file mode 100644 index 0000000000..eaae1e3798 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_ActionEndpoint_h +#define hifi_Controllers_ActionEndpoint_h + +#include "../Endpoint.h" + +#include "../../Actions.h" +#include + +#include "../../UserInputMapper.h" + +namespace controller { + +class ActionEndpoint : public Endpoint { +public: + ActionEndpoint(const Input& id = Input::INVALID_INPUT) : Endpoint(id) { } + + virtual float value() override { return _currentValue; } + virtual void apply(float newValue, float oldValue, const Pointer& source) override; + + virtual Pose pose() override { return _currentPose; } + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override; + + virtual void reset() override; + +private: + float _currentValue{ 0.0f }; + Pose _currentPose{}; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp new file mode 100644 index 0000000000..b3310e4424 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp @@ -0,0 +1,63 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "AnyEndpoint.h" + +#include "../../UserInputMapper.h" + +using namespace controller; + +AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) { + bool standard = true; + // Ensure if we're building a composite of standard devices the composite itself + // is treated as a standard device for rule processing order + for (auto endpoint : children) { + if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) { + standard = false; + break; + } + } + if (standard) { + this->_input.device = UserInputMapper::STANDARD_DEVICE; + } +} + +float AnyEndpoint::value() { + float result = 0; + for (auto& child : _children) { + float childResult = child->value(); + if (childResult != 0.0f) { + result = childResult; + } + } + return result; +} + +void AnyEndpoint::apply(float newValue, float oldValue, const Endpoint::Pointer& source) { + qFatal("AnyEndpoint is read only"); +} + +// AnyEndpoint is used for reading, so return false if any child returns false (has been written to) +bool AnyEndpoint::writeable() const { + for (auto& child : _children) { + if (!child->writeable()) { + return false; + } + } + return true; +} + +bool AnyEndpoint::readable() const { + for (auto& child : _children) { + if (!child->readable()) { + return false; + } + } + return true; +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h new file mode 100644 index 0000000000..25db6a5a2a --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_AnyEndpoint_h +#define hifi_Controllers_AnyEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class AnyEndpoint : public Endpoint { + friend class UserInputMapper; +public: + AnyEndpoint(Endpoint::List children); + virtual float value() override; + virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override; + virtual bool writeable() const override; + virtual bool readable() const override; + +private: + Endpoint::List _children; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp new file mode 100644 index 0000000000..c083a7147d --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "ArrayEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h new file mode 100644 index 0000000000..676f43868a --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h @@ -0,0 +1,43 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_ArrayEndpoint_h +#define hifi_Controllers_ArrayEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class ArrayEndpoint : public Endpoint { + friend class UserInputMapper; +public: + using Pointer = std::shared_ptr; + ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { } + + virtual float value() override { + return 0.0; + } + + virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override { + for (auto& child : _children) { + if (child->writeable()) { + child->apply(newValue, oldValue, source); + } + } + } + + virtual bool readable() const override { return false; } + +private: + Endpoint::List _children; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp new file mode 100644 index 0000000000..9d89a7d2b3 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "CompositeEndpoint.h" + +#include "../../UserInputMapper.h" + +namespace controller { + +CompositeEndpoint::CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) + : Endpoint(Input::INVALID_INPUT), Pair(first, second) { + if (first->getInput().device == UserInputMapper::STANDARD_DEVICE && + second->getInput().device == UserInputMapper::STANDARD_DEVICE) { + this->_input.device = UserInputMapper::STANDARD_DEVICE; + } +} + +float CompositeEndpoint::value() { + float result = first->value() * -1.0f + second->value(); + return result; +} + +void CompositeEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + // Composites are read only +} + +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h new file mode 100644 index 0000000000..b525a2e4ab --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_CompositeEndpoint_h +#define hifi_Controllers_CompositeEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + class CompositeEndpoint : public Endpoint, Endpoint::Pair { + public: + CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second); + + virtual float value() override; + virtual void apply(float newValue, float oldValue, const Pointer& source) override; + }; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp new file mode 100644 index 0000000000..32cd5b65e0 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "InputEndpoint.h" + +#include + +#include "../../UserInputMapper.h" + +using namespace controller; +float InputEndpoint::value(){ + _read = true; + if (isPose()) { + return pose().valid ? 1.0f : 0.0f; + } + auto userInputMapper = DependencyManager::get(); + auto deviceProxy = userInputMapper->getDeviceProxy(_input); + if (!deviceProxy) { + return 0.0f; + } + return deviceProxy->getValue(_input, 0); +} + +Pose InputEndpoint::pose() { + _read = true; + if (!isPose()) { + return Pose(); + } + auto userInputMapper = DependencyManager::get(); + auto deviceProxy = userInputMapper->getDeviceProxy(_input); + if (!deviceProxy) { + return Pose(); + } + return deviceProxy->getPose(_input, 0); +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h new file mode 100644 index 0000000000..195cd33683 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h @@ -0,0 +1,39 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_InputEndpoint_h +#define hifi_Controllers_InputEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class InputEndpoint : public Endpoint { +public: + InputEndpoint(const Input& id = Input::INVALID_INPUT) + : Endpoint(id) { + } + + virtual float value() override; + // FIXME need support for writing back to vibration / force feedback effects + virtual void apply(float newValue, float oldValue, const Pointer& source) override {} + virtual Pose pose() override; + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } + + virtual bool writeable() const { return false; } + virtual bool readable() const { return !_read; } + virtual void reset() { _read = false; } + +private: + bool _read { false }; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp new file mode 100644 index 0000000000..4560741d12 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "JSEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h new file mode 100644 index 0000000000..38ac92bfb6 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -0,0 +1,41 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_JSEndpoint_h +#define hifi_Controllers_JSEndpoint_h + +#include "../Endpoint.h" + +#include +#include + +namespace controller { + +class JSEndpoint : public Endpoint { +public: + JSEndpoint(const QJSValue& callable) + : Endpoint(Input::INVALID_INPUT), _callable(callable) { + } + + virtual float value() { + float result = (float)_callable.call().toNumber(); + return result; + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) { + _callable.call(QJSValueList({ QJSValue(newValue) })); + } + +private: + QJSValue _callable; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp new file mode 100644 index 0000000000..3dedcef4e4 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -0,0 +1,43 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "ScriptEndpoint.h" + +#include + +using namespace controller; + +float ScriptEndpoint::value() { + updateValue(); + return _lastValue; +} + +void ScriptEndpoint::updateValue() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); + return; + } + + _lastValue = (float)_callable.call().toNumber(); +} + +void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + internalApply(newValue, oldValue, source->getInput().getID()); +} + +void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, + Q_ARG(float, newValue), + Q_ARG(float, oldValue), + Q_ARG(int, sourceID)); + return; + } + _callable.call(QScriptValue(), + QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); +} diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h new file mode 100644 index 0000000000..e3c7abe812 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -0,0 +1,39 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_ScriptEndpoint_h +#define hifi_Controllers_ScriptEndpoint_h + +#include + +#include "../Endpoint.h" + +namespace controller { + +class ScriptEndpoint : public Endpoint { + Q_OBJECT; +public: + ScriptEndpoint(const QScriptValue& callable) + : Endpoint(Input::INVALID_INPUT), _callable(callable) { + } + + virtual float value(); + virtual void apply(float newValue, float oldValue, const Pointer& source); + +protected: + Q_INVOKABLE void updateValue(); + Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); +private: + QScriptValue _callable; + float _lastValue = 0.0f; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp new file mode 100644 index 0000000000..09920d249c --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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 +// + +#include "StandardEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h new file mode 100644 index 0000000000..44803a22fd --- /dev/null +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h @@ -0,0 +1,60 @@ +// +// Created by Bradley Austin Davis 2015/10/23 +// 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_StandardEndpoint_h +#define hifi_Controllers_StandardEndpoint_h + +#include "../Endpoint.h" + +namespace controller { + +class StandardEndpoint : public VirtualEndpoint { +public: + StandardEndpoint(const Input& input) : VirtualEndpoint(input) {} + virtual bool writeable() const override { return !_written; } + virtual bool readable() const override { return !_read; } + virtual void reset() override { + apply(0.0f, 0.0f, Endpoint::Pointer()); + apply(Pose(), Pose(), Endpoint::Pointer()); + _written = _read = false; + } + + virtual float value() override { + _read = true; + return VirtualEndpoint::value(); + } + + virtual void apply(float newValue, float oldValue, const Pointer& source) override { + // For standard endpoints, the first NON-ZERO write counts. + if (newValue != 0.0) { + _written = true; + } + VirtualEndpoint::apply(newValue, oldValue, source); + } + + virtual Pose pose() override { + _read = true; + return VirtualEndpoint::pose(); + } + + virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { + if (newValue != Pose()) { + _written = true; + } + VirtualEndpoint::apply(newValue, oldValue, source); + } + +private: + bool _written { false }; + bool _read { false }; +}; + +} + +#endif From d5425ac625b27bafdb01e6a9853756e80cd95856 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 13:28:36 -0700 Subject: [PATCH 145/232] Moving conditionals and endpoints out of UserInputMapper Conflicts: libraries/controllers/src/controllers/UserInputMapper.cpp --- .../src/controllers/impl/conditionals/AndConditional.cpp | 1 + .../src/controllers/impl/conditionals/AndConditional.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp index 1633f9a439..772d4f1314 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp @@ -18,3 +18,4 @@ bool AndConditional::satisfied() { } return true; } + diff --git a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h index 5fc8b7df2a..c60e4b15df 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/AndConditional.h @@ -28,5 +28,4 @@ private: } - #endif From 4e6f64833fa2cb6fcffdb2705e77d82007231274 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 11:25:06 -0700 Subject: [PATCH 146/232] Conditional support from JS --- examples/tests/controllerInterfaceTest.js | 2 + .../src/controllers/UserInputMapper.cpp | 43 ++++++++++++++++++- .../src/controllers/UserInputMapper.h | 4 ++ .../src/controllers/impl/Conditional.h | 1 + .../controllers/impl/RouteBuilderProxy.cpp | 12 +++++- .../src/controllers/impl/RouteBuilderProxy.h | 3 ++ 6 files changed, 62 insertions(+), 3 deletions(-) diff --git a/examples/tests/controllerInterfaceTest.js b/examples/tests/controllerInterfaceTest.js index 0dccd1209a..97ad9bbc38 100644 --- a/examples/tests/controllerInterfaceTest.js +++ b/examples/tests/controllerInterfaceTest.js @@ -4,8 +4,10 @@ ControllerTest = function() { var xbox = Controller.Hardware.GamePad; this.mappingEnabled = false; this.mapping = Controller.newMapping(); + this.mapping.from(standard.LX).when([standard.LB, standard.RB]).to(actions.Yaw); this.mapping.from(standard.RX).to(actions.StepYaw); this.mapping.from(standard.RY).invert().to(actions.Pitch); + this.mapping.from(standard.RY).invert().to(actions.Pitch); var testMakeAxis = false; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 19a4b78207..2579c7dbec 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -738,6 +738,47 @@ Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { return result; } + +Conditional::Pointer UserInputMapper::conditionalFor(const QJSValue& condition) { + return Conditional::Pointer(); +} + +Conditional::Pointer UserInputMapper::conditionalFor(const QScriptValue& condition) { + if (condition.isArray()) { + int length = condition.property("length").toInteger(); + Conditional::List children; + for (int i = 0; i < length; i++) { + Conditional::Pointer destination = conditionalFor(condition.property(i)); + if (!destination) { + return Conditional::Pointer(); + } + children.push_back(destination); + } + return std::make_shared(children); + } + + if (condition.isNumber()) { + return conditionalFor(Input(condition.toInt32())); + } + + if (condition.isFunction()) { + return std::make_shared(condition); + } + + qWarning() << "Unsupported conditional type " << condition.toString(); + return Conditional::Pointer(); +} + +Conditional::Pointer UserInputMapper::conditionalFor(const Input& inputId) const { + Locker locker(_lock); + auto iterator = _endpointsByInput.find(inputId); + if (_endpointsByInput.end() == iterator) { + qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); + return Conditional::Pointer(); + } + return std::make_shared(iterator->second); +} + Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { if (value.isArray()) { // Support "when" : [ "GamePad.RB", "GamePad.LB" ] @@ -764,7 +805,6 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) return Conditional::parse(value); } - Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { Filter::Pointer result; if (value.isString()) { @@ -780,7 +820,6 @@ Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { return result; } - Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { if (value.isNull()) { return Filter::List(); diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 319037fcb1..0a6ed3acad 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -143,6 +143,7 @@ namespace controller { friend class MappingBuilderProxy; void runMappings(); + static void applyRoutes(const RouteList& route); static bool applyRoute(const RoutePointer& route, bool force = false); void enableMapping(const MappingPointer& mapping); @@ -151,6 +152,9 @@ namespace controller { EndpointPointer endpointFor(const QScriptValue& endpoint); EndpointPointer endpointFor(const Input& endpoint) const; EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second); + ConditionalPointer conditionalFor(const QJSValue& endpoint); + ConditionalPointer conditionalFor(const QScriptValue& endpoint); + ConditionalPointer conditionalFor(const Input& endpoint) const; MappingPointer parseMapping(const QJsonValue& json); RoutePointer parseRoute(const QJsonValue& value); diff --git a/libraries/controllers/src/controllers/impl/Conditional.h b/libraries/controllers/src/controllers/impl/Conditional.h index 4d67d2871e..a216c8789f 100644 --- a/libraries/controllers/src/controllers/impl/Conditional.h +++ b/libraries/controllers/src/controllers/impl/Conditional.h @@ -28,6 +28,7 @@ namespace controller { using Pointer = std::shared_ptr; using List = std::list; using Factory = hifi::SimpleFactory; + using Lambda = std::function; virtual bool satisfied() = 0; virtual bool parseParameters(const QJsonValue& parameters) { return true; } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index c0d0758e4e..d56d699c28 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -33,7 +33,6 @@ void RouteBuilderProxy::to(const QScriptValue& destination) { } void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { - auto sourceEndpoint = _route->source; _route->destination = destination; _mapping->routes.push_back(_route); deleteLater(); @@ -56,6 +55,17 @@ QObject* RouteBuilderProxy::filterQml(const QJSValue& expression) { return this; } +QObject* RouteBuilderProxy::when(const QScriptValue& expression) { + _route->conditional = _parent.conditionalFor(expression); + return this; +} + +QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) { + _route->conditional = _parent.conditionalFor(expression); + return this; +} + + QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { return this; } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 2303f6184f..4bcfba5acd 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -35,9 +35,11 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE void toQml(const QJSValue& destination); Q_INVOKABLE QObject* filterQml(const QJSValue& expression); + Q_INVOKABLE QObject* whenQml(const QJSValue& expression); Q_INVOKABLE void to(const QScriptValue& destination); Q_INVOKABLE QObject* debug(bool enable = true); + Q_INVOKABLE QObject* when(const QScriptValue& expression); Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); Q_INVOKABLE QObject* pulse(float interval); @@ -49,6 +51,7 @@ class RouteBuilderProxy : public QObject { private: void to(const Endpoint::Pointer& destination); + void conditional(const Conditional::Pointer& conditional); void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); UserInputMapper& _parent; From 0959c981324e315d1e46d0a6967ae38176626e62 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 23 Oct 2015 17:26:44 -0700 Subject: [PATCH 147/232] Fixed tab --- examples/controllers/hydra/toyball.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/controllers/hydra/toyball.js b/examples/controllers/hydra/toyball.js index c3374ffb5f..10f8a82ec9 100644 --- a/examples/controllers/hydra/toyball.js +++ b/examples/controllers/hydra/toyball.js @@ -69,31 +69,31 @@ function checkControllerSide(whichSide) { var linearVelocity; var angularVelocity; var AVERAGE_FACTOR = 0.33; - var grabButtonPressed; + var grabButtonPressed; if (whichSide == LEFT_PALM) { - palmPosition = MyAvatar.leftHandPose.translation; + palmPosition = MyAvatar.leftHandPose.translation; palmRotation = MyAvatar.leftHandPose.rotation; ballAlreadyInHand = leftBallAlreadyInHand; handMessage = "LEFT"; - averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity), + averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity), Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0])); - linearVelocity = averageLinearVelocity[0]; - angularVelocity = MyAvatar.leftHandTipPose.angularVelocity; - grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5); + linearVelocity = averageLinearVelocity[0]; + angularVelocity = MyAvatar.leftHandTipPose.angularVelocity; + grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5); } else { - palmPosition = MyAvatar.rightHandPose.translation; + palmPosition = MyAvatar.rightHandPose.translation; palmRotation = MyAvatar.rightHandPose.rotation; - ballAlreadyInHand = rightBallAlreadyInHand; - averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity), + ballAlreadyInHand = rightBallAlreadyInHand; + averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity), Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1])); - linearVelocity = averageLinearVelocity[1]; - angularVelocity = MyAvatar.rightHandTipPose.angularVelocity; + linearVelocity = averageLinearVelocity[1]; + angularVelocity = MyAvatar.rightHandTipPose.angularVelocity; handMessage = "RIGHT"; - grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5); + grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5); } From 9aaef9aabd0034670c15539b45bdfaab0277b93f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:32:06 -0700 Subject: [PATCH 148/232] fix hydraPaint.js to use new API --- examples/example/painting/hydraPaint.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/examples/example/painting/hydraPaint.js b/examples/example/painting/hydraPaint.js index 29a3323e72..36137945cc 100644 --- a/examples/example/painting/hydraPaint.js +++ b/examples/example/painting/hydraPaint.js @@ -71,10 +71,8 @@ function controller(side, cycleColorButton) { this.triggerHeld = false; this.triggerThreshold = 0.9; this.side = side; - this.palm = 2 * side; - this.tip = 2 * side + 1; - this.trigger = side; - this.cycleColorButton = cycleColorButton; + this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT; + this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; this.points = []; this.normals = []; @@ -173,11 +171,10 @@ function controller(side, cycleColorButton) { this.updateControllerState = function() { - this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton); - this.palmPosition = Controller.getSpatialControlPosition(this.palm); - this.tipPosition = Controller.getSpatialControlPosition(this.tip); - this.palmNormal = Controller.getSpatialControlNormal(this.palm); - this.triggerValue = Controller.getTriggerValue(this.trigger); + this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; + this.triggerValue = Controller.getValue(this.trigger); if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { @@ -215,8 +212,8 @@ function vectorIsZero(v) { } -var rightController = new controller(RIGHT, RIGHT_BUTTON_4); -var leftController = new controller(LEFT, LEFT_BUTTON_4); +var rightController = new controller(RIGHT); +var leftController = new controller(LEFT); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); From be843a0035d820fbe7527ca7119acc60ba5682ac Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 23 Oct 2015 17:32:54 -0700 Subject: [PATCH 149/232] adding a rnning average on the velocity values returned by the hydra and adding a simple js to test --- examples/controllers/handPosesDebug.js | 102 ++++++++++++++++++ .../src/input-plugins/SixenseManager.h | 2 +- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 examples/controllers/handPosesDebug.js diff --git a/examples/controllers/handPosesDebug.js b/examples/controllers/handPosesDebug.js new file mode 100644 index 0000000000..c4e7352445 --- /dev/null +++ b/examples/controllers/handPosesDebug.js @@ -0,0 +1,102 @@ +// +// handPosesDebug.js +// examples +// +// 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 +// + + + +function makeSphere(color) { + var SPHERE_SIZE = 0.05; + var sphere = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: SPHERE_SIZE, + color: color, + alpha: 1.0, + solid: true, + visible: true, + }); + + return sphere; +} + + +var NUM_HANDS = 2; +var NUM_SPHERES_PER_HAND = 2; +var LEFT_HAND = 0; +var RIGHT_HAND = 1; + +var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ]; + +function index(handNum, indexNum) { + return handNum * NUM_HANDS + indexNum; +} + +var app = {}; + + +function setup() { + app.spheres = new Array(); + + for (var h = 0; h < NUM_HANDS; h++) { + for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) { + var i = index(h, s); + app.spheres[i] = makeSphere(COLORS[h]); + print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i])); + } + } +} + +function updateHand(handNum) { + + var pose; + var handName = "right"; + if (handNum == LEFT_HAND) { + pose = MyAvatar.getLeftHandPose(); + handName = "left"; + } else { + pose = MyAvatar.getRightHandPose(); + handName = "right"; + } + + if (pose.valid) { + //print(handName + " hand moving" + JSON.stringify(pose)); + Overlays.editOverlay(app.spheres[index(handNum, 0)], { + position: pose.translation, + visible: true, + }); + var vpos = Vec3.sum(pose.velocity, pose.translation); + Overlays.editOverlay(app.spheres[index(handNum, 1)], { + position: vpos, + visible: true, + }); + } else { + Overlays.editOverlay(app.spheres[index(handNum, 0)], { + visible: false + }); + + Overlays.editOverlay(app.spheres[index(handNum, 1)], { + visible: false + }); + } +} + +function update() { + updateHand(LEFT_HAND); + updateHand(RIGHT_HAND); +} + +function scriptEnding() { + print("Removing spheres = " + JSON.stringify(app.spheres)); + for (var i = 0; i < app.spheres.length; i++) { + Overlays.deleteOverlay(app.spheres[i]); + } +} + +setup(); +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index c0d908ed45..3bc82b365b 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -121,7 +121,7 @@ private: } }; - static const int MAX_NUM_AVERAGING_SAMPLES = 10; // At ~100 updates per seconds this means averaging over ~.1s + static const int MAX_NUM_AVERAGING_SAMPLES = 1000; // At ~100 updates per seconds this means averaging over ~.1s using MovingAverageMap = std::map< int, MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES> >; MovingAverageMap _collectedSamples; From 060f87d14a58e16cd0039863edc90e57837b8bfb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:33:45 -0700 Subject: [PATCH 150/232] CR feedback --- examples/controllers/handControllerGrab.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index a61f52117b..29d6595c3a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -867,10 +867,12 @@ function MyController(hand, triggerAction) { var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); -var mapping = Controller.newMapping("com.highfidelity.handControllerGrab"); +var MAPPING_NAME = "com.highfidelity.handControllerGrab"; + +var mapping = Controller.newMapping(MAPPING_NAME); mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger); mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger); -Controller.enableMapping("com.highfidelity.handControllerGrab"); +Controller.enableMapping(MAPPING_NAME); function update() { @@ -881,7 +883,7 @@ function update() { function cleanup() { rightController.cleanup(); leftController.cleanup(); - Controller.disableMapping("com.highfidelity.handControllerGrab"); + Controller.disableMapping(MAPPING_NAME); } Script.scriptEnding.connect(cleanup); From 4a19a1468262608d8a871c2f08574eaca0b3a2ec Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:53:34 -0700 Subject: [PATCH 151/232] fix walkApi.js to detect hydra in official way --- examples/libraries/walkApi.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 2a4471bfbf..8935380150 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -18,24 +18,8 @@ Script.include("./libraries/walkConstants.js"); Avatar = function() { // if Hydras are connected, the only way to enable use is to never set any arm joint rotation - this.hydraCheck = function() { - // function courtesy of Thijs Wenker (frisbee.js) - var numberOfButtons = Controller.getNumberOfButtons(); - var numberOfTriggers = Controller.getNumberOfTriggers(); - var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); - const HYDRA_BUTTONS = 12; - const HYDRA_TRIGGERS = 2; - const HYDRA_CONTROLLERS_PER_TRIGGER = 2; - var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; - if (numberOfButtons == HYDRA_BUTTONS && - numberOfTriggers == HYDRA_TRIGGERS && - controllersPerTrigger == HYDRA_CONTROLLERS_PER_TRIGGER) { - print('walk.js info: Razer Hydra detected. Setting arms free (not controlled by script)'); - return true; - } else { - print('walk.js info: Razer Hydra not detected. Arms will be controlled by script.'); - return false; - } + this.hydraCheck = function () { + return Controller.Hardware.Hydra !== undefined; } // settings this.headFree = true; From 0d15182adbb40bc067d60b517cdc925c6d44c19f Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 23 Oct 2015 18:09:43 -0700 Subject: [PATCH 152/232] Fixed sword.js --- examples/example/games/sword.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/example/games/sword.js b/examples/example/games/sword.js index 608fc30361..abd94b5319 100644 --- a/examples/example/games/sword.js +++ b/examples/example/games/sword.js @@ -238,7 +238,7 @@ var inHand = false; function isControllerActive() { // I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both. - controllerActive = (Vec3.length(Controller.getSpatialControlPosition(3)) > 0) || Vec3.length(Controller.getSpatialControlPosition(4)) > 0; + controllerActive = (Vec3.length(MyAvatar.leftHandPose.translation) > 0) || Vec3.length(MyAvatar.rightHandPose.translation) > 0; return controllerActive; } @@ -312,10 +312,10 @@ function grabSword(hand) { } var handRotation; if (hand === "right") { - handRotation = MyAvatar.getRightPalmRotation(); + handRotation = MyAvatar.rightHandPose.rotation; } else if (hand === "left") { - handRotation = MyAvatar.getLeftPalmRotation(); + handRotation = MyAvatar.leftHandPose.rotation; } var swordRotation = Entities.getEntityProperties(swordID).rotation; var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation); From d400c694f6d821f912002173b0c4097c54d32eaa Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 23 Oct 2015 18:09:54 -0700 Subject: [PATCH 153/232] Cleaning up for release --- examples/controllers/handPosesDebug.js | 11 +++++------ .../src/input-plugins/SixenseManager.cpp | 14 ++++++++++---- .../src/input-plugins/SixenseManager.h | 5 +++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/controllers/handPosesDebug.js b/examples/controllers/handPosesDebug.js index c4e7352445..6c933b2565 100644 --- a/examples/controllers/handPosesDebug.js +++ b/examples/controllers/handPosesDebug.js @@ -51,8 +51,7 @@ function setup() { } } -function updateHand(handNum) { - +function updateHand(handNum, deltaTime) { var pose; var handName = "right"; if (handNum == LEFT_HAND) { @@ -69,7 +68,7 @@ function updateHand(handNum) { position: pose.translation, visible: true, }); - var vpos = Vec3.sum(pose.velocity, pose.translation); + var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation); Overlays.editOverlay(app.spheres[index(handNum, 1)], { position: vpos, visible: true, @@ -85,9 +84,9 @@ function updateHand(handNum) { } } -function update() { - updateHand(LEFT_HAND); - updateHand(RIGHT_HAND); +function update(deltaTime) { + updateHand(LEFT_HAND, deltaTime); + updateHand(RIGHT_HAND, deltaTime); } function scriptEnding() { diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 1f72d66019..7ce30ec26f 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -240,7 +240,8 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { } else { auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; _poseStateMap[hand] = controller::Pose(); - _collectedSamples[hand].clear(); + _collectedSamples[hand].first.clear(); + _collectedSamples[hand].second.clear(); } } @@ -467,10 +468,15 @@ void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::q // Average auto& samples = _collectedSamples[hand]; - samples.addSample(velocity); - velocity = samples.average; + samples.first.addSample(velocity); + velocity = samples.first.average; + + // FIXME: // Not using quaternion average yet for angular velocity because it s probably wrong but keep the MovingAverage in place + //samples.second.addSample(glm::vec4(angularVelocity.x, angularVelocity.y, angularVelocity.z, angularVelocity.w)); + //angularVelocity = glm::quat(samples.second.average.w, samples.second.average.x, samples.second.average.y, samples.second.average.z); } else if (!prevPose.isValid()) { - _collectedSamples[hand].clear(); + _collectedSamples[hand].first.clear(); + _collectedSamples[hand].second.clear(); } _poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity); diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 3bc82b365b..b9ca9d8479 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -121,8 +121,9 @@ private: } }; - static const int MAX_NUM_AVERAGING_SAMPLES = 1000; // At ~100 updates per seconds this means averaging over ~.1s - using MovingAverageMap = std::map< int, MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES> >; + static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s + using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; + using MovingAverageMap = std::map< int, Samples >; MovingAverageMap _collectedSamples; #ifdef __APPLE__ From 7f3f202567b4ca36037673779c6db9bd3de8647e Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 23 Oct 2015 18:23:15 -0700 Subject: [PATCH 154/232] fix stick.js --- examples/stick.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/examples/stick.js b/examples/stick.js index f581591957..6683d8dcb6 100644 --- a/examples/stick.js +++ b/examples/stick.js @@ -10,7 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var hand = "left"; +var hand = "right"; var nullActionID = "00000000-0000-0000-0000-000000000000"; var controllerID; var controllerActive; @@ -32,7 +32,7 @@ function makeNewStick() { modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", dimensions: {x: .11, y: .11, z: 1.0}, - position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close + position: MyAvatar.rightHandPosition, // initial position doesn't matter, as long as it's close rotation: MyAvatar.orientation, damping: .1, collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav", @@ -84,23 +84,15 @@ function mouseMoveEvent(event) { } -function initControls(){ - if (hand == "right") { - controllerID = 3; // right handed - } else { - controllerID = 4; // left handed - } -} - - function update(deltaTime){ - var palmPosition = Controller.getSpatialControlPosition(controllerID); + var handPose = (hand == "right") ? MyAvatar.rightHandPose : MyAvatar.leftHandPose; + var palmPosition = handPose.translation; controllerActive = (Vec3.length(palmPosition) > 0); if(!controllerActive){ return; } - stickOrientation = Controller.getSpatialControlRawRotation(controllerID); + stickOrientation = handPose.rotation; var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); stickOrientation = Quat.multiply(stickOrientation, adjustment); From 394511ac1bb4efca011134c351af104cf4fa44ce Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 24 Oct 2015 09:50:09 -0700 Subject: [PATCH 155/232] fix frisbee.js to work with new API --- examples/controllers/hydra/frisbee.js | 34 +++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/examples/controllers/hydra/frisbee.js b/examples/controllers/hydra/frisbee.js index 78d8e77a90..46550d8e76 100644 --- a/examples/controllers/hydra/frisbee.js +++ b/examples/controllers/hydra/frisbee.js @@ -130,29 +130,38 @@ function Hand(name, palm, tip, forwardButton, button3, trigger) { this.trigger = trigger; this.holdingFrisbee = false; this.entity = false; - this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); } + this.palmPosition = function () { + return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; + }; + this.grabButtonPressed = function() { return ( - Controller.isButtonPressed(this.forwardButton) || - Controller.isButtonPressed(this.button3) || - Controller.getTriggerValue(this.trigger) > 0.5 + Controller.getValue(this.forwardButton) || + Controller.getValue(this.button3) || + Controller.getValue(this.trigger) > 0.5 ) }; - this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); }; + this.holdPosition = function () { + return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; + }; + this.holdRotation = function() { - var q = Controller.getSpatialControlRawRotation(this.palm); + var q = (this.palm == LEFT_PALM) ? Controller.getPoseValue(Controller.Standard.leftHand).rotation + : Controller.getPoseValue(Controller.Standard.rightHand).rotation; q = Quat.multiply(MyAvatar.orientation, q); return {x: q.x, y: q.y, z: q.z, w: q.w}; }; - this.tipVelocity = function() { return Controller.getSpatialControlVelocity(this.tip); }; + this.tipVelocity = function () { + return this.tip == LEFT_TIP ? MyAvatar.leftHandTipPose.velocity : MyAvatar.rightHandTipPose.velocity; + }; } function MouseControl(button) { this.button = button; } -var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0); -var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1); +var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT); +var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT); var leftMouseControl = new MouseControl("LEFT"); var middleMouseControl = new MouseControl("MIDDLE"); @@ -302,12 +311,7 @@ function initToolBar() { } function hydraCheck() { - var numberOfButtons = Controller.getNumberOfButtons(); - var numberOfTriggers = Controller.getNumberOfTriggers(); - var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); - var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; - hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2); - return true;//hydrasConnected; + return Controller.Hardware.Hydra !== undefined; } function checkController(deltaTime) { From e11b0add9a63d5497dbb30c184a18489cacb331d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 24 Oct 2015 15:29:49 -0700 Subject: [PATCH 156/232] Update safety trampoline with correct arguments. --- libraries/animation/src/Rig.cpp | 9 +++++++++ libraries/script-engine/src/ScriptEngine.cpp | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7ab39c2f34..0576a8e420 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -637,6 +637,15 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh Q_ARG(QScriptValue, _stateHandlers), Q_ARG(AnimVariantMap, _animVars), Q_ARG(AnimVariantResultHandler, handleResult)); + // It turns out that, for thread-safety reasons, ScriptEngine::callAnimationStateHandler will invoke itself if called from other + // than the script thread. Thus the above _could_ be replaced with an ordinary call, which will then trigger the same + // invokeMethod as is done explicitly above. However, the script-engine library depends on this animation library, not vice versa. + // We could create an AnimVariantCallingMixin class in shared, with an abstract virtual slot + // AnimVariantCallingMixin::callAnimationStateHandler (and move AnimVariantMap/AnimVaraintResultHandler to shared), but the + // call site here would look like this instead of the above: + // dynamic_cast(_stateHandlers.engine())->callAnimationStateHandler(_stateHandlers, _animVars, handleResult); + // This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above + // (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast. } QMutexLocker locker(&_stateMutex); // as we examine/copy most recently computed state, if any. (Typically an earlier invocation.) _animVars.copyVariantsFrom(_stateHandlersResults); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index acfa0c027b..86eb9570d8 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -748,7 +748,8 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM #endif QMetaObject::invokeMethod(this, "callAnimationStateHandler", Q_ARG(QScriptValue, callback), - Q_ARG(AnimVariantMap, parameters)); + Q_ARG(AnimVariantMap, parameters), + Q_ARG(AnimVariantResultHandler, resultHandler)); return; } QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this); From 5bc736952a6b3fb71f4558b2cbeafb44df12f292 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 24 Oct 2015 16:26:29 -0700 Subject: [PATCH 157/232] Function based endpoints should inhibit spamming with repeats of the same value --- .../src/controllers/impl/endpoints/ScriptEndpoint.cpp | 8 ++++++-- .../src/controllers/impl/endpoints/ScriptEndpoint.h | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index 3dedcef4e4..069bcb3c00 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -14,7 +14,7 @@ using namespace controller; float ScriptEndpoint::value() { updateValue(); - return _lastValue; + return _lastValueRead; } void ScriptEndpoint::updateValue() { @@ -23,14 +23,18 @@ void ScriptEndpoint::updateValue() { return; } - _lastValue = (float)_callable.call().toNumber(); + _lastValueRead = (float)_callable.call().toNumber(); } void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { + if (newValue == _lastValueWritten) { + return; + } internalApply(newValue, oldValue, source->getInput().getID()); } void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { + _lastValueWritten = newValue; if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, Q_ARG(float, newValue), diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index e3c7abe812..a56ac472be 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -31,7 +31,8 @@ protected: Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); private: QScriptValue _callable; - float _lastValue = 0.0f; + float _lastValueRead { 0.0f }; + float _lastValueWritten { 0.0f }; }; } From 3e50174114b36f8e7bd77aaf664f1091fed78b26 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 24 Oct 2015 16:26:50 -0700 Subject: [PATCH 158/232] Updating gun.js to new controller API --- examples/controllers/hydra/gun.js | 623 ++++++++++++++++-------------- 1 file changed, 331 insertions(+), 292 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index a90960a330..03caf50ad7 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -15,24 +15,61 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include([ "../../libraries/utils.js" ]); +Script.include([ "../../libraries/constants.js" ]); +Script.include([ "../../libraries/toolBars.js" ]); + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -var RED = { red: 255, green: 0, blue: 0 }; var LASER_WIDTH = 2; +var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ]; +var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ]; +var MIN_THROWER_DELAY = 1000; +var MAX_THROWER_DELAY = 1000; +var RELOAD_INTERVAL = 5; +var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4"; +var BULLET_VELOCITY = 10.0; +var GUN_OFFSETS = [ { + x: -0.04, + y: 0.26, + z: 0.04 +}, { + x: 0.04, + y: 0.26, + z: 0.04 +} ]; +var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ]; + +var BARREL_OFFSETS = [ { + x: -0.12, + y: 0.12, + z: 0.04 +}, { + x: 0.12, + y: 0.12, + z: 0.04 +} ]; + +var mapping = Controller.newMapping(); +var validPoses = [ false, false ]; +var barrelVectors = [ 0, 0 ]; +var barrelTips = [ 0, 0 ]; var pointer = []; + pointer.push(Overlays.addOverlay("line3d", { - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: RED, + start: ZERO_VECTOR, + end: ZERO_VECTOR, + color: COLORS.RED, alpha: 1, visible: true, lineWidth: LASER_WIDTH })); + pointer.push(Overlays.addOverlay("line3d", { - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: RED, + start: ZERO_VECTOR, + end: ZERO_VECTOR, + color: COLORS.RED, alpha: 1, visible: true, lineWidth: LASER_WIDTH @@ -42,56 +79,27 @@ function getRandomFloat(min, max) { return Math.random() * (max - min) + min; } -var lastX = 0; -var lastY = 0; -var yawFromMouse = 0; -var pitchFromMouse = 0; -var isMouseDown = false; - -var MIN_THROWER_DELAY = 1000; -var MAX_THROWER_DELAY = 1000; -var LEFT_BUTTON_3 = 3; -var RELOAD_INTERVAL = 5; - -var KICKBACK_ANGLE = 15; -var elbowKickAngle = 0.0; -var rotationBeforeKickback; - var showScore = false; - - -// Load some sound to use for loading and firing +// Load some sound to use for loading and firing var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); -var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4"; - var audioOptions = { - volume: 0.9 + volume: 0.9 } var shotsFired = 0; -var shotTime = new Date(); - -var activeControllers = 0; - -// initialize our controller triggers -var triggerPulled = new Array(); -var numberOfTriggers = Controller.getNumberOfTriggers(); -for (t = 0; t < numberOfTriggers; t++) { - triggerPulled[t] = false; -} - -var isLaunchButtonPressed = false; -var score = 0; +var shotTime = new Date(); +var isLaunchButtonPressed = false; +var score = 0; var bulletID = false; var targetID = false; -// Create overlay buttons and reticle +// Create overlay buttons and reticle var BUTTON_SIZE = 32; var PADDING = 3; @@ -99,78 +107,91 @@ var NUM_BUTTONS = 3; var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; -Script.include(["../../libraries/toolBars.js"]); -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) { + +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) { return { x: startX, y: (screenSize.y - (BUTTON_SIZE + PADDING)), }; }); -var reticle = Overlays.addOverlay("image", { - x: screenSize.x / 2 - (BUTTON_SIZE / 2), - y: screenSize.y / 2 - (BUTTON_SIZE / 2), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg", - alpha: 1 - }); var offButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", - alpha: 1 - }); + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", + alpha: 1 +}); startX += BUTTON_SIZE + PADDING; var platformButton = toolBar.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", - alpha: 1 - }); + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", + alpha: 1 +}); startX += BUTTON_SIZE + PADDING; var gridButton = toolBar.addOverlay("image", { - x: startX, - y: screenSize.y - (BUTTON_SIZE + PADDING), - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", - alpha: 1 - }); + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", + alpha: 1 +}); if (showScore) { var text = Overlays.addOverlay("text", { - x: screenSize.x / 2 - 100, - y: screenSize.y / 2 - 50, - width: 150, - height: 50, - color: { red: 0, green: 0, blue: 0}, - textColor: { red: 255, green: 0, blue: 0}, - topMargin: 4, - leftMargin: 4, - text: "Score: " + score - }); + x: screenSize.x / 2 - 100, + y: screenSize.y / 2 - 50, + width: 150, + height: 50, + color: { + red: 0, + green: 0, + blue: 0 + }, + textColor: { + red: 255, + green: 0, + blue: 0 + }, + topMargin: 4, + leftMargin: 4, + text: "Score: " + score + }); } -var BULLET_VELOCITY = 10.0; - function entityCollisionWithEntity(entity1, entity2, collision) { if (entity2 === targetID) { score++; if (showScore) { - Overlays.editOverlay(text, { text: "Score: " + score } ); + Overlays.editOverlay(text, { + text: "Score: " + score + }); } - // We will delete the bullet and target in 1/2 sec, but for now we can see them bounce! + // We will delete the bullet and target in 1/2 sec, but for now we can + // see them bounce! Script.setTimeout(deleteBulletAndTarget, 500); // Turn the target and the bullet white - Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }}); - Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }}); + Entities.editEntity(entity1, { + color: { + red: 255, + green: 255, + blue: 255 + } + }); + Entities.editEntity(entity2, { + color: { + red: 255, + green: 255, + blue: 255 + } + }); // play the sound near the camera so the shooter can hear it audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); @@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) { var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; - var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; + var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; + + bulletID = Entities.addEntity({ + type: "Sphere", + position: position, + dimensions: { + x: bSize, + y: bSize, + z: bSize + }, + color: { + red: 0, + green: 0, + blue: 0 + }, + velocity: bVelocity, + lifetime: BULLET_LIFETIME, + gravity: { + x: 0, + y: bGravity, + z: 0 + }, + damping: 0.01, + density: 8000, + ignoreCollisions: false, + collisionsWillMove: true + }); - bulletID = Entities.addEntity( - { type: "Sphere", - position: position, - dimensions: { x: bSize, y: bSize, z: bSize }, - color: { red: 0, green: 0, blue: 0 }, - velocity: bVelocity, - lifetime: BULLET_LIFETIME, - gravity: { x: 0, y: bGravity, z: 0 }, - damping: 0.01, - density: 8000, - ignoreCollisions: false, - collisionsWillMove: true - }); Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity); - // Play firing sounds - audioOptions.position = position; + // Play firing sounds + audioOptions.position = position; Audio.playSound(fireSound, audioOptions); shotsFired++; if ((shotsFired % RELOAD_INTERVAL) == 0) { Audio.playSound(loadSound, audioOptions); } - - // Kickback the arm - if (elbowKickAngle > 0.0) { - MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); - } - rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm"); - var armRotation = MyAvatar.getJointRotation("LeftForeArm"); - armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE)); - MyAvatar.setJointData("LeftForeArm", armRotation); - elbowKickAngle = KICKBACK_ANGLE; } + function shootTarget() { var TARGET_SIZE = 0.50; var TARGET_GRAVITY = 0.0; @@ -232,95 +257,152 @@ function shootTarget() { var DISTANCE_TO_LAUNCH_FROM = 5.0; var ANGLE_RANGE_FOR_LAUNCH = 20.0; var camera = Camera.getPosition(); - - var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); + + var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { + x: 0, + y: 1, + z: 0 + }); targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); var forwardVector = Quat.getFront(targetDirection); - + var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY); velocity.y += TARGET_UP_VELOCITY; - targetID = Entities.addEntity( - { type: "Box", - position: newPosition, - dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 }, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: velocity, - gravity: { x: 0, y: TARGET_GRAVITY, z: 0 }, - lifetime: TARGET_LIFETIME, - rotation: Camera.getOrientation(), - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); + targetID = Entities.addEntity({ + type: "Box", + position: newPosition, + dimensions: { + x: TARGET_SIZE * (0.5 + Math.random()), + y: TARGET_SIZE * (0.5 + Math.random()), + z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 + }, + color: { + red: Math.random() * 255, + green: Math.random() * 255, + blue: Math.random() * 255 + }, + velocity: velocity, + gravity: { + x: 0, + y: TARGET_GRAVITY, + z: 0 + }, + lifetime: TARGET_LIFETIME, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true + }); - // Record start time + // Record start time shotTime = new Date(); // Play target shoot sound - audioOptions.position = newPosition; + audioOptions.position = newPosition; Audio.playSound(targetLaunchSound, audioOptions); } function makeGrid(type, scale, size) { - var separation = scale * 2; + var separation = scale * 2; var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); var x, y, z; - var GRID_LIFE = 60.0; - var dimensions; + var GRID_LIFE = 60.0; + var dimensions; for (x = 0; x < size; x++) { for (y = 0; y < size; y++) { for (z = 0; z < size; z++) { - - dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; - Entities.addEntity( - { type: type, - position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, - dimensions: dimensions, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: { x: 0, y: 0, z: 0 }, - gravity: { x: 0, y: 0, z: 0 }, - lifetime: GRID_LIFE, - rotation: Camera.getOrientation(), - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); + dimensions = { + x: separation / 2.0 * (0.5 + Math.random()), + y: separation / 2.0 * (0.5 + Math.random()), + z: separation / 2.0 * (0.5 + Math.random()) / 4.0 + }; + + Entities.addEntity({ + type: type, + position: { + x: pos.x + x * separation, + y: pos.y + y * separation, + z: pos.z + z * separation + }, + dimensions: dimensions, + color: { + red: Math.random() * 255, + green: Math.random() * 255, + blue: Math.random() * 255 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + gravity: { + x: 0, + y: 0, + z: 0 + }, + lifetime: GRID_LIFE, + rotation: Camera.getOrientation(), + damping: 0.1, + density: 100.0, + collisionsWillMove: true + }); } } } } function makePlatform(gravity, scale, size) { - var separation = scale * 2; + var separation = scale * 2; var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); pos.y -= separation * size; var x, y, z; - var TARGET_LIFE = 60.0; + var TARGET_LIFE = 60.0; var INITIAL_GAP = 0.5; - var dimensions; + var dimensions; for (x = 0; x < size; x++) { for (y = 0; y < size; y++) { for (z = 0; z < size; z++) { - dimensions = { x: separation/2.0, y: separation, z: separation/2.0 }; + dimensions = { + x: separation / 2.0, + y: separation, + z: separation / 2.0 + }; - Entities.addEntity( - { type: "Box", - position: { x: pos.x - (separation * size / 2.0) + x * separation, - y: pos.y + y * (separation + INITIAL_GAP), - z: pos.z - (separation * size / 2.0) + z * separation }, - dimensions: dimensions, - color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: { x: 0, y: 0.05, z: 0 }, - gravity: { x: 0, y: gravity, z: 0 }, - lifetime: TARGET_LIFE, - damping: 0.1, - density: 100.0, - collisionsWillMove: true }); + Entities.addEntity({ + type: "Box", + position: { + x: pos.x - (separation * size / 2.0) + x * separation, + y: pos.y + y * (separation + INITIAL_GAP), + z: pos.z - (separation * size / 2.0) + z * separation + }, + dimensions: dimensions, + color: { + red: Math.random() * 255, + green: Math.random() * 255, + blue: Math.random() * 255 + }, + velocity: { + x: 0, + y: 0.05, + z: 0 + }, + gravity: { + x: 0, + y: gravity, + z: 0 + }, + lifetime: TARGET_LIFE, + damping: 0.1, + density: 100.0, + collisionsWillMove: true + }); } } } @@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) { // Make a floor for this stuff to fall onto Entities.addEntity({ type: "Box", - position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, - dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, - color: { red: 100, green: 100, blue: 100 }, + position: { + x: pos.x, + y: pos.y - separation / 2.0, + z: pos.z + }, + dimensions: { + x: 2.0 * separation * size, + y: separation / 2.0, + z: 2.0 * separation * size + }, + color: { + red: 100, + green: 100, + blue: 100 + }, lifetime: TARGET_LIFE }); @@ -340,7 +434,7 @@ function keyPressEvent(event) { // if our tools are off, then don't do anything if (event.text == "t") { var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; - Script.setTimeout(shootTarget, time); + Script.setTimeout(shootTarget, time); } else if ((event.text == ".") || (event.text == "SPACE")) { shootFromMouse(false); } else if (event.text == ",") { @@ -348,7 +442,7 @@ function keyPressEvent(event) { } else if (event.text == "r") { playLoadSound(); } else if (event.text == "s") { - // Hit this key to dump a posture from hydra to log + // Hit this key to dump a posture from hydra to log Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); @@ -356,137 +450,70 @@ function keyPressEvent(event) { } function playLoadSound() { - audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); + audioOptions.position = MyAvatar.leftHandPose.translation; Audio.playSound(loadSound, audioOptions); - // Raise arm to firing posture - takeFiringPose(); -} - -function clearPose() { - MyAvatar.clearJointData("LeftForeArm"); - MyAvatar.clearJointData("LeftArm"); - MyAvatar.clearJointData("LeftHand"); } function deleteBulletAndTarget() { Entities.deleteEntity(bulletID); Entities.deleteEntity(targetID); - bulletID = false; - targetID = false; + bulletID = false; + targetID = false; } -function takeFiringPose() { - clearPose(); - if (Controller.getNumberOfSpatialControls() == 0) { - MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843}); - MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219}); - MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333}); - } -} - -MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40); -MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40); - -// Give a bit of time to load before playing sound -Script.setTimeout(playLoadSound, 2000); - function update(deltaTime) { - if (activeControllers == 0) { - if (Controller.getNumberOfSpatialControls() > 0) { - activeControllers = Controller.getNumberOfSpatialControls(); - clearPose(); - } - } + // FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2] + var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ]; - var KICKBACK_DECAY_RATE = 0.125; - if (elbowKickAngle > 0.0) { - if (elbowKickAngle > 0.5) { - var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE; - elbowKickAngle -= newAngle; - var armRotation = MyAvatar.getJointRotation("LeftForeArm"); - armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle)); - MyAvatar.setJointData("LeftForeArm", armRotation); - } else { - MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); - if (Controller.getNumberOfSpatialControls() > 0) { - clearPose(); - } - elbowKickAngle = 0.0; - } - } - - - // check for trigger press - - var numberOfTriggers = 2; - var controllersPerTrigger = 2; - - if (numberOfTriggers == 2 && controllersPerTrigger == 2) { - for (var t = 0; t < 2; t++) { - var shootABullet = false; - var triggerValue = Controller.getTriggerValue(t); - if (triggerPulled[t]) { - // must release to at least 0.1 - if (triggerValue < 0.1) { - triggerPulled[t] = false; // unpulled - } - } else { - // must pull to at least - if (triggerValue > 0.5) { - triggerPulled[t] = true; // pulled - shootABullet = true; - } - } - var palmController = t * controllersPerTrigger; - var palmPosition = Controller.getSpatialControlPosition(palmController); - var fingerTipController = palmController + 1; - var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController); - var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition); - - // Update Lasers - Overlays.editOverlay(pointer[t], { - start: palmPosition, - end: laserTip, - alpha: 1 + for (var side = 0; side < 2; side++) { + // First check if the controller is valid + var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]); + validPoses[side] = controllerPose.valid; + if (!controllerPose.valid) { + Overlays.editOverlay(pointer[side], { + visible: false }); - - if (shootABullet) { - - var palmToFingerTipVector = - { x: (fingerTipPosition.x - palmPosition.x), - y: (fingerTipPosition.y - palmPosition.y), - z: (fingerTipPosition.z - palmPosition.z) }; - - // just off the front of the finger tip - var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2, - y: fingerTipPosition.y + palmToFingerTipVector.y/2, - z: fingerTipPosition.z + palmToFingerTipVector.z/2}; - - var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector)); - - shootBullet(position, velocity, false); - } + continue; } + + // Need to adjust the laser + var tipPose = tipPoses[side]; + var handRotation = MyAvatar.getJointCombinedRotation(side == 0 ? "LeftHand" : "RightHand"); + // handRotation = Quat.multiply(Quat.inverse(MyAvatar.orientation), + // handRotation); + var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]); + barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset); + barrelVectors[side] = Vec3.multiplyQbyV(handRotation, { + x: 0, + y: 1, + z: 0 + }); + + var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]); + // Update Lasers + Overlays.editOverlay(pointer[side], { + start: barrelTips[side], + end: laserTip, + alpha: 1, + visible: true + }); } } -function shootFromMouse(grenade) { - var DISTANCE_FROM_CAMERA = 1.0; - var camera = Camera.getPosition(); - var forwardVector = Quat.getFront(Camera.getOrientation()); - var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); - var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); - shootBullet(newPosition, velocity, grenade); -} - -function mouseReleaseEvent(event) { - // position - isMouseDown = false; +function triggerChanged(side, value) { + var pressed = (value != 0); + if (pressed) { + var position = barrelTips[side]; + var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side])); + shootBullet(position, velocity, false); + } } function mousePressEvent(event) { - var clickedText = false; - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (clickedOverlay == offButton) { Script.stop(); } else if (clickedOverlay == platformButton) { @@ -494,25 +521,37 @@ function mousePressEvent(event) { makePlatform(-9.8, 1.0, platformSize); } else if (clickedOverlay == gridButton) { makeGrid("Box", 1.0, 3); - } + } } function scriptEnding() { - Overlays.deleteOverlay(reticle); + mapping.disable(); toolBar.cleanup(); - Overlays.deleteOverlay(pointer[0]); - Overlays.deleteOverlay(pointer[1]); + for (var i = 0; i < pointer.length; ++i) { + Overlays.deleteOverlay(pointer[i]); + } Overlays.deleteOverlay(text); - MyAvatar.detachOne(gunModel); - MyAvatar.detachOne(gunModel); + MyAvatar.detachOne(GUN_MODEL); + MyAvatar.detachOne(GUN_MODEL); clearPose(); } +MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[1], 0.40); +MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40); + +// Give a bit of time to load before playing sound +Script.setTimeout(playLoadSound, 2000); + +mapping.from(Controller.Standard.LT).constrainToPositiveInteger().to(function(value) { + triggerChanged(0, value); +}); + +mapping.from(Controller.Standard.RT).constrainToPositiveInteger().to(function(value) { + triggerChanged(1, value); +}); +mapping.enable(); + Script.scriptEnding.connect(scriptEnding); Script.update.connect(update); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mousePressEvent.connect(mousePressEvent); Controller.keyPressEvent.connect(keyPressEvent); - - - From a7547ef11e5ab1e70f56511215ddc7b13a6a1e97 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 25 Oct 2015 16:44:43 -0700 Subject: [PATCH 159/232] Adding hysteresis filter, moving filters to individual files --- .../src/controllers/impl/Filter.cpp | 226 ++++++++++++------ .../controllers/src/controllers/impl/Filter.h | 198 +-------------- .../controllers/impl/RouteBuilderProxy.cpp | 52 ++-- .../src/controllers/impl/RouteBuilderProxy.h | 4 +- .../controllers/impl/filters/ClampFilter.cpp | 38 +++ .../controllers/impl/filters/ClampFilter.h | 32 +++ .../impl/filters/ConstrainToIntegerFilter.cpp | 9 + .../impl/filters/ConstrainToIntegerFilter.h | 30 +++ .../ConstrainToPositiveIntegerFilter.cpp | 9 + .../ConstrainToPositiveIntegerFilter.h | 30 +++ .../impl/filters/DeadZoneFilter.cpp | 26 ++ .../controllers/impl/filters/DeadZoneFilter.h | 31 +++ .../impl/filters/HysteresisFilter.cpp | 57 +++++ .../impl/filters/HysteresisFilter.h | 31 +++ .../controllers/impl/filters/InvertFilter.cpp | 9 + .../controllers/impl/filters/InvertFilter.h | 29 +++ .../controllers/impl/filters/PulseFilter.cpp | 37 +++ .../controllers/impl/filters/PulseFilter.h | 36 +++ .../controllers/impl/filters/ScaleFilter.cpp | 19 ++ .../controllers/impl/filters/ScaleFilter.h | 34 +++ 20 files changed, 638 insertions(+), 299 deletions(-) create mode 100644 libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/ClampFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/InvertFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/PulseFilter.h create mode 100644 libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp create mode 100644 libraries/controllers/src/controllers/impl/filters/ScaleFilter.h diff --git a/libraries/controllers/src/controllers/impl/Filter.cpp b/libraries/controllers/src/controllers/impl/Filter.cpp index bc31e9ea44..09188318eb 100644 --- a/libraries/controllers/src/controllers/impl/Filter.cpp +++ b/libraries/controllers/src/controllers/impl/Filter.cpp @@ -14,101 +14,183 @@ #include #include -#include "SharedUtil.h" +#include + +#include "filters/ClampFilter.h" +#include "filters/ConstrainToIntegerFilter.h" +#include "filters/ConstrainToPositiveIntegerFilter.h" +#include "filters/DeadZoneFilter.h" +#include "filters/HysteresisFilter.h" +#include "filters/InvertFilter.h" +#include "filters/PulseFilter.h" +#include "filters/ScaleFilter.h" using namespace controller; Filter::Factory Filter::_factory; -REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") +REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp") REGISTER_FILTER_CLASS_INSTANCE(ConstrainToIntegerFilter, "constrainToInteger") REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger") -REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") -REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp") REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") +REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") +REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") +REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") - const QString JSON_FILTER_TYPE = QStringLiteral("type"); const QString JSON_FILTER_PARAMS = QStringLiteral("params"); -Filter::Pointer Filter::parse(const QJsonObject& json) { - // The filter is an object, now let s check for type and potential arguments +Filter::Pointer Filter::parse(const QJsonValue& json) { Filter::Pointer filter; - auto filterType = json[JSON_FILTER_TYPE]; - if (filterType.isString()) { + if (json.isString()) { + filter = Filter::getFactory().create(json.toString()); + } else if (json.isObject()) { + QJsonObject jsonObj = json.toObject(); + // The filter is an object, now let s check for type and potential arguments + auto filterType = jsonObj[JSON_FILTER_TYPE]; filter = Filter::getFactory().create(filterType.toString()); if (filter) { - // Filter is defined, need to read the parameters and validate - auto parameters = json[JSON_FILTER_PARAMS]; - if (parameters.isArray()) { - if (filter->parseParameters(parameters.toArray())) { - } + QJsonValue params = jsonObj; + if (jsonObj.contains(JSON_FILTER_PARAMS)) { + params = jsonObj[JSON_FILTER_PARAMS]; + } + if (!filter->parseParameters(params)) { + qWarning() << "Unable to parse filter parameters " << params; + return Filter::Pointer(); } - - return filter; } } - return Filter::Pointer(); + return filter; } - -bool ScaleFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _scale = parameters[0].toDouble(); - } - return true; -} - - -bool ClampFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _min = parameters[0].toDouble(); - } - if (parameters.size() > 2) { - _max = parameters[1].toDouble(); - } - return true; -} - - -float DeadZoneFilter::apply(float value) const { - float scale = 1.0f / (1.0f - _min); - if (std::abs(value) < _min) { - return 0.0f; - } - return (value - _min) * scale; -} - -bool DeadZoneFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _min = parameters[0].toDouble(); - } - return true; -} - - -float PulseFilter::apply(float value) const { - float result = 0.0f; - - if (0.0f != value) { - float now = secTimestampNow(); - float delta = now - _lastEmitTime; - if (delta >= _interval) { - _lastEmitTime = now; - result = value; +bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output) { + if (parameters.isDouble()) { + output = parameters.toDouble(); + return true; + } else if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() > 1) { + output = arrayParameters[0].toDouble(); + return true; } - } - - return result; -} - -bool PulseFilter::parseParameters(const QJsonArray& parameters) { - if (parameters.size() > 1) { - _interval = parameters[0].toDouble(); - } - return true; + } else if (parameters.isObject()) { + static const QString JSON_MIN = QStringLiteral("interval"); + auto objectParameters = parameters.toObject(); + if (objectParameters.contains(name)) { + output = objectParameters[name].toDouble(); + return true; + } + } + return false; } + +#if 0 + +namespace controller { + + class LambdaFilter : public Filter { + public: + // LambdaFilter() {}12 + LambdaFilter(Lambda f) : _function(f) {}; + + virtual float apply(float value) const { + return _function(value); + } + + virtual bool parseParameters(const QJsonArray& parameters) { return true; } + + // REGISTER_FILTER_CLASS(LambdaFilter); + private: + Lambda _function; + }; + + class ScriptFilter : public Filter { + public: + + }; + + + + //class EasingFilter : public Filter { + //public: + // virtual float apply(float value) const override; + + //private: + // QEasingCurve _curve; + //}; + + //// GLSL style filters + //class StepFilter : public Filter { + //public: + // StepFilter(float edge) : _edge(edge) {}; + // virtual float apply(float value) const override; + + //private: + // const float _edge; + //}; + + //class PowFilter : public Filter { + //public: + // PowFilter(float exponent) : _exponent(exponent) {}; + // virtual float apply(float value) const override; + + //private: + // const float _exponent; + //}; + + //class AbsFilter : public Filter { + //public: + // virtual float apply(float value) const override; + //}; + + //class SignFilter : public Filter { + //public: + // virtual float apply(float value) const override; + //}; + + //class FloorFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return floor(newValue); + // } + //}; + + //class CeilFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return ceil(newValue); + // } + //}; + + //class FractFilter : public Filter { + //public: + // virtual float apply(float value) const override { + // return fract(newValue); + // } + //}; + + //class MinFilter : public Filter { + //public: + // MinFilter(float mine) : _min(min) {}; + + // virtual float apply(float value) const override { + // return glm::min(_min, newValue); + // } + + //private: + // const float _min; + //}; + + //class MaxFilter : public Filter { + //public: + // MaxFilter(float max) : _max(max) {}; + // virtual float apply(float newValue, float oldValue) override; + //private: + // const float _max; + //}; +} +#endif \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/Filter.h b/libraries/controllers/src/controllers/impl/Filter.h index 1fa9833044..77585c8ebb 100644 --- a/libraries/controllers/src/controllers/impl/Filter.h +++ b/libraries/controllers/src/controllers/impl/Filter.h @@ -21,8 +21,7 @@ #include -class QJsonObject; -class QJsonArray; +class QJsonValue; namespace controller { @@ -36,11 +35,13 @@ namespace controller { virtual float apply(float value) const = 0; // Factory features - virtual bool parseParameters(const QJsonArray& parameters) { return true; } + virtual bool parseParameters(const QJsonValue& parameters) { return true; } - static Pointer parse(const QJsonObject& json); + static Pointer parse(const QJsonValue& json); static void registerBuilder(const QString& name, Factory::Builder builder); static Factory& getFactory() { return _factory; } + + static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); protected: static Factory _factory; }; @@ -54,194 +55,5 @@ namespace controller { #define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \ classEntry::Registrar classEntry::_registrar(className, Filter::getFactory()); -namespace controller { - - class LambdaFilter : public Filter { - public: - // LambdaFilter() {} - LambdaFilter(Lambda f) : _function(f) {}; - - virtual float apply(float value) const { - return _function(value); - } - - virtual bool parseParameters(const QJsonArray& parameters) { return true; } - -// REGISTER_FILTER_CLASS(LambdaFilter); - private: - Lambda _function; - }; - - class ScriptFilter : public Filter { - public: - - }; - - class ScaleFilter : public Filter { - REGISTER_FILTER_CLASS(ScaleFilter); - public: - ScaleFilter() {} - ScaleFilter(float scale): _scale(scale) {} - - virtual float apply(float value) const override { - return value * _scale; - } - virtual bool parseParameters(const QJsonArray& parameters); - - private: - float _scale = 1.0f; - }; - - class InvertFilter : public ScaleFilter { - REGISTER_FILTER_CLASS(InvertFilter); - public: - InvertFilter() : ScaleFilter(-1.0f) {} - - virtual bool parseParameters(const QJsonArray& parameters) { return true; } - - private: - }; - - class ClampFilter : public Filter { - REGISTER_FILTER_CLASS(ClampFilter); - public: - ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; - - virtual float apply(float value) const override { - return glm::clamp(value, _min, _max); - } - virtual bool parseParameters(const QJsonArray& parameters) override; - protected: - float _min = 0.0f; - float _max = 1.0f; - }; - - class DeadZoneFilter : public Filter { - REGISTER_FILTER_CLASS(DeadZoneFilter); - public: - DeadZoneFilter(float min = 0.0) : _min(min) {}; - - virtual float apply(float value) const override; - virtual bool parseParameters(const QJsonArray& parameters) override; - protected: - float _min = 0.0f; - }; - - class PulseFilter : public Filter { - REGISTER_FILTER_CLASS(PulseFilter); - public: - PulseFilter() {} - PulseFilter(float interval) : _interval(interval) {} - - - virtual float apply(float value) const override; - - virtual bool parseParameters(const QJsonArray& parameters); - - private: - mutable float _lastEmitTime{ -::std::numeric_limits::max() }; - float _interval = 1.0f; - }; - - class ConstrainToIntegerFilter : public Filter { - REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); - public: - ConstrainToIntegerFilter() {}; - - virtual float apply(float value) const override { - return glm::sign(value); - } - protected: - }; - - class ConstrainToPositiveIntegerFilter : public Filter { - REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); - public: - ConstrainToPositiveIntegerFilter() {}; - - virtual float apply(float value) const override { - return (value <= 0.0f) ? 0.0f : 1.0f; - } - protected: - }; - - //class EasingFilter : public Filter { - //public: - // virtual float apply(float value) const override; - - //private: - // QEasingCurve _curve; - //}; - - //// GLSL style filters - //class StepFilter : public Filter { - //public: - // StepFilter(float edge) : _edge(edge) {}; - // virtual float apply(float value) const override; - - //private: - // const float _edge; - //}; - - //class PowFilter : public Filter { - //public: - // PowFilter(float exponent) : _exponent(exponent) {}; - // virtual float apply(float value) const override; - - //private: - // const float _exponent; - //}; - - //class AbsFilter : public Filter { - //public: - // virtual float apply(float value) const override; - //}; - - //class SignFilter : public Filter { - //public: - // virtual float apply(float value) const override; - //}; - - //class FloorFilter : public Filter { - //public: - // virtual float apply(float value) const override { - // return floor(newValue); - // } - //}; - - //class CeilFilter : public Filter { - //public: - // virtual float apply(float value) const override { - // return ceil(newValue); - // } - //}; - - //class FractFilter : public Filter { - //public: - // virtual float apply(float value) const override { - // return fract(newValue); - // } - //}; - - //class MinFilter : public Filter { - //public: - // MinFilter(float mine) : _min(min) {}; - - // virtual float apply(float value) const override { - // return glm::min(_min, newValue); - // } - - //private: - // const float _min; - //}; - - //class MaxFilter : public Filter { - //public: - // MaxFilter(float max) : _max(max) {}; - // virtual float apply(float newValue, float oldValue) override; - //private: - // const float _max; - //}; -} #endif diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index d56d699c28..49e615439d 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -18,6 +18,15 @@ #include "../ScriptingInterface.h" #include "../Logging.h" +#include "filters/ClampFilter.h" +#include "filters/ConstrainToIntegerFilter.h" +#include "filters/ConstrainToPositiveIntegerFilter.h" +#include "filters/DeadZoneFilter.h" +#include "filters/HysteresisFilter.h" +#include "filters/InvertFilter.h" +#include "filters/PulseFilter.h" +#include "filters/ScaleFilter.h" + using namespace controller; void RouteBuilderProxy::toQml(const QJSValue& destination) { @@ -43,18 +52,6 @@ QObject* RouteBuilderProxy::debug(bool enable) { return this; } -QObject* RouteBuilderProxy::filterQml(const QJSValue& expression) { - if (expression.isCallable()) { - addFilter([=](float value) { - QJSValue originalExpression = expression; - QJSValueList params({ QJSValue(value) }); - auto result = originalExpression.call(params); - return (float)(result.toNumber()); - }); - } - return this; -} - QObject* RouteBuilderProxy::when(const QScriptValue& expression) { _route->conditional = _parent.conditionalFor(expression); return this; @@ -65,53 +62,46 @@ QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) { return this; } - -QObject* RouteBuilderProxy::filter(const QScriptValue& expression) { - return this; -} - - QObject* RouteBuilderProxy::clamp(float min, float max) { - addFilter(Filter::Pointer(new ClampFilter(min, max))); + addFilter(std::make_shared(min, max)); return this; } QObject* RouteBuilderProxy::scale(float multiplier) { - addFilter(Filter::Pointer(new ScaleFilter(multiplier))); + addFilter(std::make_shared(multiplier)); return this; } QObject* RouteBuilderProxy::invert() { - addFilter(Filter::Pointer(new InvertFilter())); + addFilter(std::make_shared()); + return this; +} + +QObject* RouteBuilderProxy::hysteresis(float min, float max) { + addFilter(std::make_shared(min, max)); return this; } QObject* RouteBuilderProxy::deadZone(float min) { - addFilter(Filter::Pointer(new DeadZoneFilter(min))); + addFilter(std::make_shared(min)); return this; } QObject* RouteBuilderProxy::constrainToInteger() { - addFilter(Filter::Pointer(new ConstrainToIntegerFilter())); + addFilter(std::make_shared()); return this; } QObject* RouteBuilderProxy::constrainToPositiveInteger() { - addFilter(Filter::Pointer(new ConstrainToPositiveIntegerFilter())); + addFilter(std::make_shared()); return this; } - QObject* RouteBuilderProxy::pulse(float interval) { - addFilter(Filter::Pointer(new PulseFilter(interval))); + addFilter(std::make_shared(interval)); return this; } -void RouteBuilderProxy::addFilter(Filter::Lambda lambda) { - Filter::Pointer filterPointer = std::make_shared < LambdaFilter > (lambda); - addFilter(filterPointer); -} - void RouteBuilderProxy::addFilter(Filter::Pointer filter) { _route->filters.push_back(filter); } diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 4bcfba5acd..d55aa80f6b 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -34,14 +34,13 @@ class RouteBuilderProxy : public QObject { : _parent(parent), _mapping(mapping), _route(route) { } Q_INVOKABLE void toQml(const QJSValue& destination); - Q_INVOKABLE QObject* filterQml(const QJSValue& expression); Q_INVOKABLE QObject* whenQml(const QJSValue& expression); Q_INVOKABLE void to(const QScriptValue& destination); Q_INVOKABLE QObject* debug(bool enable = true); Q_INVOKABLE QObject* when(const QScriptValue& expression); - Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); + Q_INVOKABLE QObject* hysteresis(float min, float max); Q_INVOKABLE QObject* pulse(float interval); Q_INVOKABLE QObject* scale(float multiplier); Q_INVOKABLE QObject* invert(); @@ -52,7 +51,6 @@ class RouteBuilderProxy : public QObject { private: void to(const Endpoint::Pointer& destination); void conditional(const Conditional::Pointer& conditional); - void addFilter(Filter::Lambda lambda); void addFilter(Filter::Pointer filter); UserInputMapper& _parent; Mapping::Pointer _mapping; diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp new file mode 100644 index 0000000000..ec22981ef3 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp @@ -0,0 +1,38 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "ClampFilter.h" + +#include +#include + +using namespace controller; + +bool ClampFilter::parseParameters(const QJsonValue& parameters) { + if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() > 1) { + _min = arrayParameters[0].toDouble(); + } + if (arrayParameters.size() > 2) { + _max = arrayParameters[1].toDouble(); + } + } else if (parameters.isObject()) { + static const QString JSON_MAX = QStringLiteral("max"); + static const QString JSON_MIN = QStringLiteral("min"); + + auto objectParameters = parameters.toObject(); + if (objectParameters.contains(JSON_MIN)) { + _min = objectParameters[JSON_MIN].toDouble(); + } + if (objectParameters.contains(JSON_MAX)) { + _max = objectParameters[JSON_MAX].toDouble(); + } + } + return true; +} diff --git a/libraries/controllers/src/controllers/impl/filters/ClampFilter.h b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h new file mode 100644 index 0000000000..fd82821b3e --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ClampFilter.h @@ -0,0 +1,32 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_Clamp_h +#define hifi_Controllers_Filters_Clamp_h + +#include "../Filter.h" + +namespace controller { + +class ClampFilter : public Filter { + REGISTER_FILTER_CLASS(ClampFilter); +public: + ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; + virtual float apply(float value) const override { + return glm::clamp(value, _min, _max); + } + virtual bool parseParameters(const QJsonValue& parameters) override; +protected: + float _min = 0.0f; + float _max = 1.0f; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp new file mode 100644 index 0000000000..78ffb47693 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "ConstrainToIntegerFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h new file mode 100644 index 0000000000..580dc2a856 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_ConstrainToIntegerFilter_h +#define hifi_Controllers_Filters_ConstrainToIntegerFilter_h + +#include "../Filter.h" + +namespace controller { + +class ConstrainToIntegerFilter : public Filter { + REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); +public: + ConstrainToIntegerFilter() {}; + + virtual float apply(float value) const override { + return glm::sign(value); + } +protected: +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp new file mode 100644 index 0000000000..d78942b18f --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "ConstrainToPositiveIntegerFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h new file mode 100644 index 0000000000..27395cde24 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_ConstrainToPositiveInteger_h +#define hifi_Controllers_Filters_ConstrainToPositiveInteger_h + +#include "../Filter.h" + +namespace controller { + +class ConstrainToPositiveIntegerFilter : public Filter { + REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); +public: + ConstrainToPositiveIntegerFilter() {}; + + virtual float apply(float value) const override { + return (value <= 0.0f) ? 0.0f : 1.0f; + } +protected: +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp new file mode 100644 index 0000000000..809308eeab --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "DeadZoneFilter.h" + +#include +#include + +using namespace controller; +float DeadZoneFilter::apply(float value) const { + float scale = 1.0f / (1.0f - _min); + if (std::abs(value) < _min) { + return 0.0f; + } + return (value - _min) * scale; +} + +bool DeadZoneFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_MIN = QStringLiteral("min"); + return parseSingleFloatParameter(parameters, JSON_MIN, _min); +} diff --git a/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h new file mode 100644 index 0000000000..70ac657415 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_DeadZoneFilter_h +#define hifi_Controllers_Filters_DeadZoneFilter_h + +#include "../Filter.h" + +namespace controller { + +class DeadZoneFilter : public Filter { + REGISTER_FILTER_CLASS(DeadZoneFilter); +public: + DeadZoneFilter(float min = 0.0) : _min(min) {}; + + virtual float apply(float value) const override; + virtual bool parseParameters(const QJsonValue& parameters) override; +protected: + float _min = 0.0f; +}; + + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp new file mode 100644 index 0000000000..c1edfe35da --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp @@ -0,0 +1,57 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "HysteresisFilter.h" + +#include +#include + +using namespace controller; + +float HysteresisFilter::apply(float value) const { + if (_signaled) { + if (value <= _min) { + _signaled = false; + } + } else { + if (value >= _max) { + _signaled = true; + } + } + return _signaled ? 1.0f : 0.0f; +} + +bool HysteresisFilter::parseParameters(const QJsonValue& parameters) { + if (parameters.isArray()) { + auto arrayParameters = parameters.toArray(); + if (arrayParameters.size() > 1) { + _min = arrayParameters[0].toDouble(); + } + if (arrayParameters.size() > 2) { + _max = arrayParameters[1].toDouble(); + } + } else if (parameters.isObject()) { + static const QString JSON_MAX = QStringLiteral("max"); + static const QString JSON_MIN = QStringLiteral("min"); + + auto objectParameters = parameters.toObject(); + if (objectParameters.contains(JSON_MIN)) { + _min = objectParameters[JSON_MIN].toDouble(); + } + if (objectParameters.contains(JSON_MAX)) { + _max = objectParameters[JSON_MAX].toDouble(); + } + } else { + return false; + } + + if (_min > _max) { + std::swap(_min, _max); + } + return true; +} diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h new file mode 100644 index 0000000000..cf33f1bbff --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h @@ -0,0 +1,31 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_Hysteresis_h +#define hifi_Controllers_Filters_Hysteresis_h + +#include "../Filter.h" + +namespace controller { + +class HysteresisFilter : public Filter { + REGISTER_FILTER_CLASS(HysteresisFilter); +public: + HysteresisFilter(float min = 0.25, float max = 0.75) : _min(min), _max(max) {}; + virtual float apply(float value) const override; + virtual bool parseParameters(const QJsonValue& parameters) override; +protected: + float _min; + float _max; + mutable bool _signaled { false }; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp new file mode 100644 index 0000000000..db582b84cc --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "InvertFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h new file mode 100644 index 0000000000..889cd0140c --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_InvertFilter_h +#define hifi_Controllers_Filters_InvertFilter_h + +#include "ScaleFilter.h" + +namespace controller { + +class InvertFilter : public ScaleFilter { + REGISTER_FILTER_CLASS(InvertFilter); +public: + InvertFilter() : ScaleFilter(-1.0f) {} + + virtual bool parseParameters(const QJsonArray& parameters) { return true; } + +private: +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp b/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp new file mode 100644 index 0000000000..f4e1f04791 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "PulseFilter.h" + +#include +#include + +using namespace controller; + + + +float PulseFilter::apply(float value) const { + float result = 0.0f; + + if (0.0f != value) { + float now = secTimestampNow(); + float delta = now - _lastEmitTime; + if (delta >= _interval) { + _lastEmitTime = now; + result = value; + } + } + + return result; +} + +bool PulseFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_MIN = QStringLiteral("interval"); + return parseSingleFloatParameter(parameters, JSON_MIN, _interval); +} + diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h new file mode 100644 index 0000000000..2512b479cf --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h @@ -0,0 +1,36 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_Pulse_h +#define hifi_Controllers_Filters_Pulse_h + +#include "../Filter.h" + +namespace controller { + + +class PulseFilter : public Filter { + REGISTER_FILTER_CLASS(PulseFilter); +public: + PulseFilter() {} + PulseFilter(float interval) : _interval(interval) {} + + + virtual float apply(float value) const override; + + virtual bool parseParameters(const QJsonValue& parameters); + +private: + mutable float _lastEmitTime { -::std::numeric_limits::max() }; + float _interval = 1.0f; +}; + +} + +#endif diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp new file mode 100644 index 0000000000..4a310e3a04 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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 +// + +#include "ScaleFilter.h" + +#include +#include + +using namespace controller; + +bool ScaleFilter::parseParameters(const QJsonValue& parameters) { + static const QString JSON_SCALE = QStringLiteral("scale"); + return parseSingleFloatParameter(parameters, JSON_SCALE, _scale); +} diff --git a/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h new file mode 100644 index 0000000000..39c5edd4e5 --- /dev/null +++ b/libraries/controllers/src/controllers/impl/filters/ScaleFilter.h @@ -0,0 +1,34 @@ +// +// Created by Bradley Austin Davis 2015/10/25 +// 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_Filters_Scale_h +#define hifi_Controllers_Filters_Scale_h + +#include "../Filter.h" + +namespace controller { + +class ScaleFilter : public Filter { + REGISTER_FILTER_CLASS(ScaleFilter); +public: + ScaleFilter() {} + ScaleFilter(float scale) : _scale(scale) {} + + virtual float apply(float value) const override { + return value * _scale; + } + virtual bool parseParameters(const QJsonValue& parameters); + +private: + float _scale = 1.0f; +}; + +} + +#endif From 75b2ee94b243f34f7c7fab4c8bfe6f6dce627816 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 25 Oct 2015 16:44:55 -0700 Subject: [PATCH 160/232] PR feedback --- examples/controllers/hydra/gun.js | 27 +++++++------------ .../impl/filters/HysteresisFilter.cpp | 7 +++++ .../impl/filters/HysteresisFilter.h | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 03caf50ad7..576c4335f6 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -15,9 +15,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include([ "../../libraries/utils.js" ]); -Script.include([ "../../libraries/constants.js" ]); -Script.include([ "../../libraries/toolBars.js" ]); +// FIXME kickback functionality was removed because the joint setting interface in +// MyAvatar has apparently changed, breaking it. + +Script.include("../../libraries/utils.js"); +Script.include("../../libraries/constants.js"); +Script.include("../../libraries/toolBars.js"); HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -100,14 +103,11 @@ var bulletID = false; var targetID = false; // Create overlay buttons and reticle - var BUTTON_SIZE = 32; var PADDING = 3; var NUM_BUTTONS = 3; - var screenSize = Controller.getViewportDimensions(); var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; - var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) { return { x: startX, @@ -441,11 +441,6 @@ function keyPressEvent(event) { shootFromMouse(true); } else if (event.text == "r") { playLoadSound(); - } else if (event.text == "s") { - // Hit this key to dump a posture from hydra to log - Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); - Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); - Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); } } @@ -478,9 +473,7 @@ function update(deltaTime) { // Need to adjust the laser var tipPose = tipPoses[side]; - var handRotation = MyAvatar.getJointCombinedRotation(side == 0 ? "LeftHand" : "RightHand"); - // handRotation = Quat.multiply(Quat.inverse(MyAvatar.orientation), - // handRotation); + var handRotation = tipPoses[side].rotation; var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]); barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset); barrelVectors[side] = Vec3.multiplyQbyV(handRotation, { @@ -536,17 +529,17 @@ function scriptEnding() { clearPose(); } -MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[1], 0.40); +MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40); MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40); // Give a bit of time to load before playing sound Script.setTimeout(playLoadSound, 2000); -mapping.from(Controller.Standard.LT).constrainToPositiveInteger().to(function(value) { +mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) { triggerChanged(0, value); }); -mapping.from(Controller.Standard.RT).constrainToPositiveInteger().to(function(value) { +mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) { triggerChanged(1, value); }); mapping.enable(); diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp index c1edfe35da..a7f22e1de4 100644 --- a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp @@ -13,6 +13,13 @@ using namespace controller; +HysteresisFilter::HysteresisFilter(float min, float max) : _min(min), _max(max) { + if (_min > _max) { + std::swap(_min, _max); + } +}; + + float HysteresisFilter::apply(float value) const { if (_signaled) { if (value <= _min) { diff --git a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h index cf33f1bbff..4f7e07928d 100644 --- a/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h @@ -17,7 +17,7 @@ namespace controller { class HysteresisFilter : public Filter { REGISTER_FILTER_CLASS(HysteresisFilter); public: - HysteresisFilter(float min = 0.25, float max = 0.75) : _min(min), _max(max) {}; + HysteresisFilter(float min = 0.25, float max = 0.75); virtual float apply(float value) const override; virtual bool parseParameters(const QJsonValue& parameters) override; protected: From 900b07fdeef094f5e6ddf14939fc1a76934ae628 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 26 Oct 2015 10:00:07 -0700 Subject: [PATCH 161/232] dead code --- interface/src/avatar/MyAvatar.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 435e6af5ef..1ffb27930e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -118,8 +118,6 @@ public: Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); } // Removes a handler previously added by addAnimationStateHandler. Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } - // Processes a handler result. Not really for user code, but used by callAnimationStateHandler. - Q_INVOKABLE void animationStateHandlerResult(QScriptValue handler, QScriptValue result) { _rig->animationStateHandlerResult(handler, result); } // get/set avatar data void saveData(); From 4b4907c9ef8bb4398d1db9cb7c8285c823c758e5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 26 Oct 2015 10:04:55 -0700 Subject: [PATCH 162/232] Allow multiple scripts to register, and allow them to specify the specific anim vars they are interested in. --- libraries/animation/src/AnimVariant.h | 4 +- libraries/animation/src/AnimVariantMap.cpp | 31 +++++++--- libraries/animation/src/Rig.cpp | 60 +++++++++++++------- libraries/animation/src/Rig.h | 12 +++- libraries/script-engine/src/ScriptEngine.cpp | 11 ++-- libraries/script-engine/src/ScriptEngine.h | 2 +- 6 files changed, 79 insertions(+), 41 deletions(-) diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 7a80321f7b..bd96dda77d 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -163,7 +163,7 @@ public: bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. - QScriptValue animVariantMapToScriptValue(QScriptEngine* engine) const; + QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const; // Side-effect us with the value of object's own properties. (No inherited properties.) void animVariantMapFromScriptValue(const QScriptValue& object); void copyVariantsFrom(const AnimVariantMap& other); @@ -206,7 +206,7 @@ protected: std::set _triggers; }; -typedef std::function AnimVariantResultHandler; +typedef std::function AnimVariantResultHandler; Q_DECLARE_METATYPE(AnimVariantResultHandler); Q_DECLARE_METATYPE(AnimVariantMap) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index 59f10a96e0..0c808bd404 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -15,36 +15,49 @@ #include #include "AnimVariant.h" -QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine) const { +QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { if (QThread::currentThread() != engine->thread()) { qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); return QScriptValue(); } QScriptValue target = engine->newObject(); - for (auto& pair : _map) { - switch (pair.second.getType()) { + auto setOne = [&] (QString name, AnimVariant value) { + switch (value.getType()) { case AnimVariant::Type::Bool: - target.setProperty(pair.first, pair.second.getBool()); + target.setProperty(name, value.getBool()); break; case AnimVariant::Type::Int: - target.setProperty(pair.first, pair.second.getInt()); + target.setProperty(name, value.getInt()); break; case AnimVariant::Type::Float: - target.setProperty(pair.first, pair.second.getFloat()); + target.setProperty(name, value.getFloat()); break; case AnimVariant::Type::String: - target.setProperty(pair.first, pair.second.getString()); + target.setProperty(name, value.getString()); break; case AnimVariant::Type::Vec3: - target.setProperty(pair.first, vec3toScriptValue(engine, pair.second.getVec3())); + target.setProperty(name, vec3toScriptValue(engine, value.getVec3())); break; case AnimVariant::Type::Quat: - target.setProperty(pair.first, quatToScriptValue(engine, pair.second.getQuat())); + target.setProperty(name, quatToScriptValue(engine, value.getQuat())); break; default: // Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now. assert("AnimVariant::Type" == "valid"); } + }; + if (useNames) { // copy only the requested names + for (const QString& name : names) { + auto search = _map.find(name); + if (search != _map.end()) { // scripts are allowed to request names that do not exist + setOne(name, search->second); + } + } + + } else { // copy all of them + for (auto& pair : _map) { + setOne(pair.first, pair.second); + } } return target; } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0576a8e420..7810bb8693 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -606,36 +606,51 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } // Allow script to add/remove handlers and report results, from within their thread. -// TODO: iterate multiple handlers, but with one shared arg. -// TODO: fill the properties based on the union of requested properties. (Keep all properties objs and compute new union when add/remove handler.) QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread - _stateHandlers = handler; - return handler; // suitable for giving to removeAnimationStateHandler -} -void Rig::removeAnimationStateHandler(QScriptValue handler) { // called in script thread - _stateHandlers = QScriptValue(); - QMutexLocker locker(&_stateMutex); // guarding access to results - _stateHandlersResults.clearMap(); // TODO: When we have multiple handlers, we'll need to clear only his handler's results. -} -void Rig::animationStateHandlerResult(QScriptValue handler, QScriptValue result) { // called synchronously from script - // handler is currently ignored but might be used in storing individual results QMutexLocker locker(&_stateMutex); - if (!_stateHandlers.isValid()) { + int identifier = ++_nextStateHandlerId; // 0 is unused + StateHandler& data = _stateHandlers[identifier]; + data.function = handler; + data.useNames = propertiesList.isArray(); + if (data.useNames) { + data.propertyNames = propertiesList.toVariant().toStringList(); + } + return QScriptValue(identifier); // suitable for giving to removeAnimationStateHandler +} +void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread + QMutexLocker locker(&_stateMutex); + _stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present +} +void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script + QMutexLocker locker(&_stateMutex); + auto found = _stateHandlers.find(identifier); + if (found == _stateHandlers.end()) { return; // Don't use late-breaking results that got reported after the handler was removed. } - _stateHandlersResults.animVariantMapFromScriptValue(result); // Into our own copy. + found.value().results.animVariantMapFromScriptValue(result); // Into our own copy. } void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread) - if (_stateHandlers.isValid()) { - auto handleResult = [this](QScriptValue handler, QScriptValue result) { - animationStateHandlerResult(handler, result); + QMutexLocker locker(&_stateMutex); + // It might pay to produce just one AnimVariantMap copy here, with a union of all the requested propertyNames, + // rather than having each callAnimationStateHandler invocation make its own copy. + // However, that copying is done on the script's own time rather than ours, so even if it's less cpu, it would be more + // work on the avatar update thread (which is possibly the main thread). + for (auto data = _stateHandlers.begin(); data != _stateHandlers.end(); data++) { + // call out: + int identifier = data.key(); + StateHandler& value = data.value(); + QScriptValue& function = value.function; + auto handleResult = [this, identifier](QScriptValue result) { + animationStateHandlerResult(identifier, result); }; // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture // the state of _animVars and allow continued changes to _animVars in this thread without conflict. - QMetaObject::invokeMethod(_stateHandlers.engine(), "callAnimationStateHandler", Qt::QueuedConnection, - Q_ARG(QScriptValue, _stateHandlers), + QMetaObject::invokeMethod(function.engine(), "callAnimationStateHandler", Qt::QueuedConnection, + Q_ARG(QScriptValue, function), Q_ARG(AnimVariantMap, _animVars), + Q_ARG(QStringList, value.propertyNames), + Q_ARG(bool, value.useNames), Q_ARG(AnimVariantResultHandler, handleResult)); // It turns out that, for thread-safety reasons, ScriptEngine::callAnimationStateHandler will invoke itself if called from other // than the script thread. Thus the above _could_ be replaced with an ordinary call, which will then trigger the same @@ -643,12 +658,13 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh // We could create an AnimVariantCallingMixin class in shared, with an abstract virtual slot // AnimVariantCallingMixin::callAnimationStateHandler (and move AnimVariantMap/AnimVaraintResultHandler to shared), but the // call site here would look like this instead of the above: - // dynamic_cast(_stateHandlers.engine())->callAnimationStateHandler(_stateHandlers, _animVars, handleResult); + // dynamic_cast(function.engine())->callAnimationStateHandler(function, ..., handleResult); // This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above // (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast. + + // gather results in (likely from an earlier update): + _animVars.copyVariantsFrom(value.results); // If multiple handlers write the same anim var, the last registgered wins. (_map preserves order). } - QMutexLocker locker(&_stateMutex); // as we examine/copy most recently computed state, if any. (Typically an earlier invocation.) - _animVars.copyVariantsFrom(_stateHandlersResults); } void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 83f1a02b8a..5551b21e40 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -53,6 +53,12 @@ typedef std::shared_ptr RigPointer; class Rig : public QObject, public std::enable_shared_from_this { public: + struct StateHandler { + AnimVariantMap results; + QStringList propertyNames; + QScriptValue function; + bool useNames; + }; struct HeadParameters { float leanSideways = 0.0f; // degrees @@ -203,7 +209,7 @@ public: bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); void removeAnimationStateHandler(QScriptValue handler); - void animationStateHandlerResult(QScriptValue handler, QScriptValue result); + void animationStateHandlerResult(int identifier, QScriptValue result); bool getModelOffset(glm::vec3& modelOffsetOut) const; @@ -249,8 +255,8 @@ public: float _rightHandOverlayAlpha = 0.0f; private: - QScriptValue _stateHandlers; - AnimVariantMap _stateHandlersResults; + QMap _stateHandlers; + int _nextStateHandlerId {0}; QMutex _stateMutex; }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 86eb9570d8..91df9fa793 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -262,7 +262,8 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { // callAnimationStateHandler requires that the type be registered. // These two are meaningful, if we ever do want to use them... static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { - return parameters.animVariantMapToScriptValue(engine); + QStringList unused; + return parameters.animVariantMapToScriptValue(engine, unused, false); } static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) { parameters.animVariantMapFromScriptValue(value); @@ -741,7 +742,7 @@ void ScriptEngine::stop() { } // Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. -void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, AnimVariantResultHandler resultHandler) { +void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; @@ -749,14 +750,16 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM QMetaObject::invokeMethod(this, "callAnimationStateHandler", Q_ARG(QScriptValue, callback), Q_ARG(AnimVariantMap, parameters), + Q_ARG(QStringList, names), + Q_ARG(bool, useNames), Q_ARG(AnimVariantResultHandler, resultHandler)); return; } - QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this); + QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this, names, useNames); QScriptValueList callingArguments; callingArguments << javascriptParametgers; QScriptValue result = callback.call(QScriptValue(), callingArguments); - resultHandler(callback, result); + resultHandler(result); } void ScriptEngine::timerFired() { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index b6f736c846..30468880a2 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -144,7 +144,7 @@ public: ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } public slots: - void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, AnimVariantResultHandler resultHandler); + void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); signals: void scriptLoaded(const QString& scriptFilename); From f38303a9a5890c6fa75a73d776089dabd4b2d27e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 10:36:31 -0700 Subject: [PATCH 163/232] Adding vive default mapping --- interface/resources/controllers/vive.json | 22 +++++++++++++++++++ .../input-plugins/ViveControllerManager.cpp | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 interface/resources/controllers/vive.json diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json new file mode 100644 index 0000000000..f51f908813 --- /dev/null +++ b/interface/resources/controllers/vive.json @@ -0,0 +1,22 @@ +{ + "name": "XBox to Standard", + "channels": [ + { "from": "Vive.LY", "to": "Standard.LY" }, + { "from": "Vive.LX", "to": "Standard.LX" }, + { "from": "Vive.LT", "to": "Standard.LT" }, + { "from": "Vive.LB", "to": "Standard.LB" }, + { "from": "Vive.LS", "to": "Standard.LS" }, + + { "from": "Vive.RY", "to": "Standard.RY" }, + { "from": "Vive.RX", "to": "Standard.RX" }, + { "from": "Vive.RT", "to": "Standard.RT" }, + { "from": "Vive.RB", "to": "Standard.RB" }, + { "from": "Vive.RS", "to": "Standard.RS" }, + + { "from": "Vive.Back", "to": "Standard.Back" }, + { "from": "Vive.Start", "to": "Standard.Start" }, + + { "from": "Vive.A", "to": "Standard.A" }, + { "from": "Vive.B", "to": "Standard.B" } + ] +} diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 21b7b81173..8dd3d21a07 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -45,7 +45,7 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString RENDER_CONTROLLERS = "Render Hand Controllers"; ViveControllerManager::ViveControllerManager() : - InputDevice("SteamVR Controller"), + InputDevice("Vive"), _trackedControllers(0), _modelLoaded(false), _leftHandRenderID(0), @@ -313,7 +313,7 @@ void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, boo if (button == vr::k_EButton_ApplicationMenu) { // FIXME? - _buttonPressedMap.insert(left ? controller::A : controller::A); + _buttonPressedMap.insert(left ? controller::B : controller::A); } else if (button == vr::k_EButton_Grip) { // Tony says these are harder to reach, so make them the meta buttons _buttonPressedMap.insert(left ? controller::BACK : controller::START); From 6b9b4d4e6e38d6c8428261c1699f3074ecedc570 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 10:44:39 -0700 Subject: [PATCH 164/232] Add libraries/line.js --- examples/libraries/line.js | 149 +++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 examples/libraries/line.js diff --git a/examples/libraries/line.js b/examples/libraries/line.js new file mode 100644 index 0000000000..62811e50bc --- /dev/null +++ b/examples/libraries/line.js @@ -0,0 +1,149 @@ +function error(message) { + print("[ERROR] " + message); +} + +// PolyLine +var LINE_DIMENSIONS = { x: 2000, y: 2000, z: 2000 }; +var MAX_LINE_LENGTH = 40; // This must be 2 or greater; +var PolyLine = function(position, color, defaultStrokeWidth) { + this.position = position; + this.color = color; + this.defaultStrokeWidth = 0.10; + this.points = [ + { x: 0, y: 0, z: 0 } + ]; + this.strokeWidths = [ + this.defaultStrokeWidth + ]; + this.normals = [ + { x: 1, y: 0, z: 0 } + ] + this.entityID = Entities.addEntity({ + type: "PolyLine", + position: position, + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + dimensions: LINE_DIMENSIONS, + color: color, + lifetime: 20, + }); +}; + +PolyLine.prototype.enqueuePoint = function(position) { + if (this.isFull()) { + error("Hit max PolyLine size"); + return; + } + + position = Vec3.subtract(position, this.position); + this.points.push(position); + this.normals.push({ x: 1, y: 0, z: 0 }); + this.strokeWidths.push(this.defaultStrokeWidth * Math.min(1.0, this.points.length / 10)); + Entities.editEntity(this.entityID, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + }); +}; + +PolyLine.prototype.dequeuePoint = function() { + if (this.points.length == 0) { + error("Hit min PolyLine size"); + return; + } + + this.points = this.points.slice(1); + this.normals = this.normals.slice(1); + this.strokeWidths = this.strokeWidths.slice(1); + + Entities.editEntity(this.entityID, { + linePoints: this.points, + normals: this.normals, + strokeWidths: this.strokeWidths, + }); +}; + +PolyLine.prototype.getFirstPoint = function() { + return Vec3.sum(this.position, this.points[0]); +}; + +PolyLine.prototype.getLastPoint = function() { + return Vec3.sum(this.position, this.points[this.points.length - 1]); +}; + +PolyLine.prototype.getSize = function() { + return this.points.length; +} + +PolyLine.prototype.isFull = function() { + return this.points.length >= MAX_LINE_LENGTH; +}; + +PolyLine.prototype.destroy = function() { + Entities.deleteEntity(this.entityID); + this.points = []; +}; + + + +// InfiniteLine +InfiniteLine = function(position, color) { + this.position = position; + this.color = color; + this.lines = [new PolyLine(position, color)]; + this.size = 0; +}; + +InfiniteLine.prototype.enqueuePoint = function(position) { + var currentLine; + + if (this.lines.length == 0) { + currentLine = new PolyLine(position, this.color); + this.lines.push(currentLine); + } else { + currentLine = this.lines[this.lines.length - 1]; + } + + if (currentLine.isFull()) { + var newLine = new PolyLine(currentLine.getLastPoint(), this.color); + this.lines.push(newLine); + currentLine = newLine; + } + + currentLine.enqueuePoint(position); + + ++this.size; +}; + +InfiniteLine.prototype.dequeuePoint = function() { + if (this.lines.length == 0) { + error("Trying to dequeue from InfiniteLine when no points are left"); + return; + } + + var lastLine = this.lines[0]; + lastLine.dequeuePoint(); + + if (lastLine.getSize() <= 1) { + this.lines = this.lines.slice(1); + } + + --this.size; +}; + +InfiniteLine.prototype.getFirstPoint = function() { + return this.lines.length > 0 ? this.lines[0].getFirstPoint() : null; +}; + +InfiniteLine.prototype.getLastPoint = function() { + return this.lines.length > 0 ? this.lines[lines.length - 1].getLastPoint() : null; +}; + +InfiniteLine.prototype.destroy = function() { + for (var i = 0; i < this.lines.length; ++i) { + this.lines[i].destroy(); + } + + this.size = 0; +}; From d6230fbed329f5db5780adca536012939d035a4c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 10:46:36 -0700 Subject: [PATCH 165/232] Remove trailing commas in line.js --- examples/libraries/line.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/libraries/line.js b/examples/libraries/line.js index 62811e50bc..0834c6176e 100644 --- a/examples/libraries/line.js +++ b/examples/libraries/line.js @@ -26,7 +26,7 @@ var PolyLine = function(position, color, defaultStrokeWidth) { strokeWidths: this.strokeWidths, dimensions: LINE_DIMENSIONS, color: color, - lifetime: 20, + lifetime: 20 }); }; @@ -43,7 +43,7 @@ PolyLine.prototype.enqueuePoint = function(position) { Entities.editEntity(this.entityID, { linePoints: this.points, normals: this.normals, - strokeWidths: this.strokeWidths, + strokeWidths: this.strokeWidths }); }; @@ -60,7 +60,7 @@ PolyLine.prototype.dequeuePoint = function() { Entities.editEntity(this.entityID, { linePoints: this.points, normals: this.normals, - strokeWidths: this.strokeWidths, + strokeWidths: this.strokeWidths }); }; From 691e5f3be0880f54872e4143f0e2dc05891cb5d7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 11:07:54 -0700 Subject: [PATCH 166/232] Remove new/old value division from endpoint::apply --- .../src/controllers/UserInputMapper.cpp | 4 ++-- .../controllers/src/controllers/impl/Endpoint.h | 12 ++++++------ .../impl/endpoints/ActionEndpoint.cpp | 6 +++--- .../controllers/impl/endpoints/ActionEndpoint.h | 4 ++-- .../controllers/impl/endpoints/AnyEndpoint.cpp | 2 +- .../src/controllers/impl/endpoints/AnyEndpoint.h | 2 +- .../controllers/impl/endpoints/ArrayEndpoint.h | 4 ++-- .../impl/endpoints/CompositeEndpoint.cpp | 2 +- .../impl/endpoints/CompositeEndpoint.h | 2 +- .../controllers/impl/endpoints/InputEndpoint.h | 4 ++-- .../src/controllers/impl/endpoints/JSEndpoint.h | 2 +- .../impl/endpoints/ScriptEndpoint.cpp | 15 +++++++-------- .../controllers/impl/endpoints/ScriptEndpoint.h | 4 ++-- .../impl/endpoints/StandardEndpoint.h | 16 ++++++++-------- 14 files changed, 39 insertions(+), 40 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 2579c7dbec..4a055e3c3f 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -529,7 +529,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { } } // no filters yet for pose - destination->apply(value, Pose(), source); + destination->apply(value, source); } else { // Fetch the value, may have been overriden by previous loopback routes float value = getValue(source); @@ -546,7 +546,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { qCDebug(controllers) << "Filtered value was " << value; } - destination->apply(value, 0, source); + destination->apply(value, source); } return true; } diff --git a/libraries/controllers/src/controllers/impl/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h index f5fe058d82..5dd3f6adb4 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -37,9 +37,9 @@ namespace controller { Endpoint(const Input& input) : _input(input) {} virtual float value() = 0; - virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; + virtual void apply(float value, const Pointer& source) = 0; virtual Pose pose() { return Pose(); } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) {} + virtual void apply(const Pose& value, const Pointer& source) {} virtual const bool isPose() { return _input.isPose(); } virtual bool writeable() const { return true; } @@ -58,7 +58,7 @@ namespace controller { : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } virtual float value() override { return _readLambda(); } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { _writeLambda(newValue); } + virtual void apply(float value, const Pointer& source) override { _writeLambda(value); } private: ReadLambda _readLambda; @@ -73,11 +73,11 @@ namespace controller { } virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { _currentValue = newValue; } + virtual void apply(float value, const Pointer& source) override { _currentValue = value; } virtual Pose pose() override { return _currentPose; } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - _currentPose = newValue; + virtual void apply(const Pose& value, const Pointer& source) override { + _currentPose = value; } protected: float _currentValue { 0.0f }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp index d07ef38185..b671d8e93c 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp @@ -14,7 +14,7 @@ using namespace controller; -void ActionEndpoint::apply(float newValue, float oldValue, const Pointer& source) { +void ActionEndpoint::apply(float newValue, const Pointer& source) { _currentValue += newValue; if (_input != Input::INVALID_INPUT) { auto userInputMapper = DependencyManager::get(); @@ -22,8 +22,8 @@ void ActionEndpoint::apply(float newValue, float oldValue, const Pointer& source } } -void ActionEndpoint::apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) { - _currentPose = newValue; +void ActionEndpoint::apply(const Pose& value, const Pointer& source) { + _currentPose = value; if (!_currentPose.isValid()) { return; } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h index eaae1e3798..574fdcedb5 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h @@ -24,10 +24,10 @@ public: ActionEndpoint(const Input& id = Input::INVALID_INPUT) : Endpoint(id) { } virtual float value() override { return _currentValue; } - virtual void apply(float newValue, float oldValue, const Pointer& source) override; + virtual void apply(float newValue, const Pointer& source) override; virtual Pose pose() override { return _currentPose; } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override; + virtual void apply(const Pose& value, const Pointer& source) override; virtual void reset() override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp index b3310e4424..24f3479ea8 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp @@ -38,7 +38,7 @@ float AnyEndpoint::value() { return result; } -void AnyEndpoint::apply(float newValue, float oldValue, const Endpoint::Pointer& source) { +void AnyEndpoint::apply(float newValue, const Endpoint::Pointer& source) { qFatal("AnyEndpoint is read only"); } diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h index 25db6a5a2a..86dd057414 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h @@ -19,7 +19,7 @@ class AnyEndpoint : public Endpoint { public: AnyEndpoint(Endpoint::List children); virtual float value() override; - virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override; + virtual void apply(float newValue, const Endpoint::Pointer& source) override; virtual bool writeable() const override; virtual bool readable() const override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h index 676f43868a..79bb604ec0 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h @@ -24,10 +24,10 @@ public: return 0.0; } - virtual void apply(float newValue, float oldValue, const Endpoint::Pointer& source) override { + virtual void apply(float value, const Endpoint::Pointer& source) override { for (auto& child : _children) { if (child->writeable()) { - child->apply(newValue, oldValue, source); + child->apply(value, source); } } } diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp index 9d89a7d2b3..e5088ef72c 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp @@ -25,7 +25,7 @@ float CompositeEndpoint::value() { return result; } -void CompositeEndpoint::apply(float newValue, float oldValue, const Pointer& source) { +void CompositeEndpoint::apply(float newValue, const Pointer& source) { // Composites are read only } diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h index b525a2e4ab..ab8b97aa50 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h @@ -18,7 +18,7 @@ namespace controller { CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second); virtual float value() override; - virtual void apply(float newValue, float oldValue, const Pointer& source) override; + virtual void apply(float newValue, const Pointer& source) override; }; } diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h index 195cd33683..d58f0c2e73 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h @@ -22,9 +22,9 @@ public: virtual float value() override; // FIXME need support for writing back to vibration / force feedback effects - virtual void apply(float newValue, float oldValue, const Pointer& source) override {} + virtual void apply(float newValue, const Pointer& source) override {} virtual Pose pose() override; - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } + virtual void apply(const Pose& value, const Pointer& source) override { } virtual bool writeable() const { return false; } virtual bool readable() const { return !_read; } diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h index 38ac92bfb6..27f17b5cd3 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -28,7 +28,7 @@ public: return result; } - virtual void apply(float newValue, float oldValue, const Pointer& source) { + virtual void apply(float newValue, const Pointer& source) { _callable.call(QJSValueList({ QJSValue(newValue) })); } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index 069bcb3c00..d9b4a5fc59 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -26,22 +26,21 @@ void ScriptEndpoint::updateValue() { _lastValueRead = (float)_callable.call().toNumber(); } -void ScriptEndpoint::apply(float newValue, float oldValue, const Pointer& source) { - if (newValue == _lastValueWritten) { +void ScriptEndpoint::apply(float value, const Pointer& source) { + if (value == _lastValueWritten) { return; } - internalApply(newValue, oldValue, source->getInput().getID()); + internalApply(value, source->getInput().getID()); } -void ScriptEndpoint::internalApply(float newValue, float oldValue, int sourceID) { - _lastValueWritten = newValue; +void ScriptEndpoint::internalApply(float value, int sourceID) { + _lastValueWritten = value; if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, - Q_ARG(float, newValue), - Q_ARG(float, oldValue), + Q_ARG(float, value), Q_ARG(int, sourceID)); return; } _callable.call(QScriptValue(), - QScriptValueList({ QScriptValue(newValue), QScriptValue(oldValue), QScriptValue(sourceID) })); + QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index a56ac472be..23f77892c6 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -24,11 +24,11 @@ public: } virtual float value(); - virtual void apply(float newValue, float oldValue, const Pointer& source); + virtual void apply(float newValue, const Pointer& source); protected: Q_INVOKABLE void updateValue(); - Q_INVOKABLE virtual void internalApply(float newValue, float oldValue, int sourceID); + Q_INVOKABLE virtual void internalApply(float newValue, int sourceID); private: QScriptValue _callable; float _lastValueRead { 0.0f }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h index 44803a22fd..74adaf825d 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h @@ -20,8 +20,8 @@ public: virtual bool writeable() const override { return !_written; } virtual bool readable() const override { return !_read; } virtual void reset() override { - apply(0.0f, 0.0f, Endpoint::Pointer()); - apply(Pose(), Pose(), Endpoint::Pointer()); + apply(0.0f, Endpoint::Pointer()); + apply(Pose(), Endpoint::Pointer()); _written = _read = false; } @@ -30,12 +30,12 @@ public: return VirtualEndpoint::value(); } - virtual void apply(float newValue, float oldValue, const Pointer& source) override { + virtual void apply(float value, const Pointer& source) override { // For standard endpoints, the first NON-ZERO write counts. - if (newValue != 0.0) { + if (value != 0.0) { _written = true; } - VirtualEndpoint::apply(newValue, oldValue, source); + VirtualEndpoint::apply(value, source); } virtual Pose pose() override { @@ -43,11 +43,11 @@ public: return VirtualEndpoint::pose(); } - virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { - if (newValue != Pose()) { + virtual void apply(const Pose& value, const Pointer& source) override { + if (value != Pose()) { _written = true; } - VirtualEndpoint::apply(newValue, oldValue, source); + VirtualEndpoint::apply(value, source); } private: From a8872d065b2b4aa680b49b86b99680b43e8c31a0 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 11:23:13 -0700 Subject: [PATCH 167/232] MOve the movingAverage class to shared next to SimpleMovingAverage --- .../src/input-plugins/SixenseManager.h | 27 ++---------------- libraries/shared/src/SimpleMovingAverage.h | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index b9ca9d8479..c6b938c774 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -24,6 +24,8 @@ #endif +#include + #include #include @@ -95,31 +97,6 @@ private: float _lastDistance; bool _useSixenseFilter = true; - template class MovingAverage { - public: - using Samples = std::list< T >; - Samples samples; - T average; - - void clear() { - samples.clear(); - } - - bool isAverageValid() const { return !samples.empty(); } - - void addSample(T sample) { - samples.push_front(sample); - int numSamples = samples.size(); - - if (numSamples < MAX_NUM_SAMPLES) { - average = (sample + average * float(numSamples - 1)) / float(numSamples); - } else { - T tail = samples.back(); - samples.pop_back(); - average = average + (sample - tail) / float(numSamples); - } - } - }; static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 194a078194..5f4e7367fc 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -15,6 +15,7 @@ #define hifi_SimpleMovingAverage_h #include +#include class SimpleMovingAverage { public: @@ -40,4 +41,31 @@ private: float ONE_MINUS_WEIGHTING; }; + +template class MovingAverage { +public: + using Samples = std::list< T >; + Samples samples; + T average; + + void clear() { + samples.clear(); + } + + bool isAverageValid() const { return !samples.empty(); } + + void addSample(T sample) { + samples.push_front(sample); + int numSamples = samples.size(); + + if (numSamples < MAX_NUM_SAMPLES) { + average = (sample + average * (float)(numSamples - 1)) / (float)(numSamples); + } else { + T tail = samples.back(); + samples.pop_back(); + average = average + (sample - tail) / (float)(numSamples); + } + } +}; + #endif // hifi_SimpleMovingAverage_h From 34b517839373a212fdcca1b0096e0ba80af5f23a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 11:55:23 -0700 Subject: [PATCH 168/232] Fixing ping-ping entity script --- examples/toybox/ping_pong_gun/pingPongGun.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/toybox/ping_pong_gun/pingPongGun.js b/examples/toybox/ping_pong_gun/pingPongGun.js index 879d467293..48b82f0a36 100644 --- a/examples/toybox/ping_pong_gun/pingPongGun.js +++ b/examples/toybox/ping_pong_gun/pingPongGun.js @@ -45,6 +45,12 @@ green: 255, blue: 255 }; + + var TRIGGER_CONTROLS = [ + Controller.Standard.LT, + Controller.Standard.RT, + ]; + PingPongGun.prototype = { hand: null, @@ -53,11 +59,11 @@ canShoot: false, canShootTimeout: null, setRightHand: function() { - this.hand = 'RIGHT'; + this.hand = 1; }, setLeftHand: function() { - this.hand = 'LEFT'; + this.hand = 0; }, startNearGrab: function() { @@ -92,12 +98,7 @@ }, checkTriggerPressure: function(gunHand) { - var handClickString = gunHand + "_HAND_CLICK"; - - var handClick = Controller.findAction(handClickString); - - this.triggerValue = Controller.getActionValue(handClick); - + this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]); if (this.triggerValue < RELOAD_THRESHOLD) { // print('RELOAD'); this.canShoot = true; From d91d0a20271c0ffdc063e37ea20c59b9080c7815 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 12:47:31 -0700 Subject: [PATCH 169/232] Fixing spray can script --- examples/toybox/spray_paint/sprayPaintCan.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/toybox/spray_paint/sprayPaintCan.js b/examples/toybox/spray_paint/sprayPaintCan.js index 4e6719af76..60fd12b975 100644 --- a/examples/toybox/spray_paint/sprayPaintCan.js +++ b/examples/toybox/spray_paint/sprayPaintCan.js @@ -33,12 +33,17 @@ var MIN_POINT_DISTANCE = 0.01; var STROKE_WIDTH = 0.02; + var TRIGGER_CONTROLS = [ + Controller.Standard.LT, + Controller.Standard.RT, + ]; + this.setRightHand = function () { - this.hand = 'RIGHT'; + this.hand = 1; } this.setLeftHand = function () { - this.hand = 'LEFT'; + this.hand = 0; } this.startNearGrab = function () { @@ -46,11 +51,7 @@ } this.toggleWithTriggerPressure = function () { - var handClickString = this.whichHand + "_HAND_CLICK"; - - var handClick = Controller.findAction(handClickString); - - this.triggerValue = Controller.getActionValue(handClick); + this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.whichHand]); if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) { this.spraying = false; this.disableStream(); From 990764d85577ed5f9e167b3fab91bbb6f1176407 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 13:29:13 -0700 Subject: [PATCH 170/232] Really fixing the MovingAverage class... --- libraries/shared/src/SimpleMovingAverage.h | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 5f4e7367fc..53754ae241 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -15,7 +15,6 @@ #define hifi_SimpleMovingAverage_h #include -#include class SimpleMovingAverage { public: @@ -44,27 +43,24 @@ private: template class MovingAverage { public: - using Samples = std::list< T >; - Samples samples; + const float WEIGHTING = 1.0f / (float)MAX_NUM_SAMPLES; + const float ONE_MINUS_WEIGHTING = 1.0f - WEIGHTING; + int numSamples{ 0 }; T average; void clear() { - samples.clear(); + numSamples = 0; } - bool isAverageValid() const { return !samples.empty(); } + bool isAverageValid() const { return (numSamples > 0); } void addSample(T sample) { - samples.push_front(sample); - int numSamples = samples.size(); - - if (numSamples < MAX_NUM_SAMPLES) { - average = (sample + average * (float)(numSamples - 1)) / (float)(numSamples); + if (numSamples > 0) { + average = (sample * WEIGHTING) + (average * ONE_MINUS_WEIGHTING); } else { - T tail = samples.back(); - samples.pop_back(); - average = average + (sample - tail) / (float)(numSamples); + average = sample; } + numSamples++; } }; From 1a1ab29978e04302fb49aff32a2afcfd3611b147 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 16:45:43 -0700 Subject: [PATCH 171/232] Add lineExample.js --- examples/example/lineExample.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 examples/example/lineExample.js diff --git a/examples/example/lineExample.js b/examples/example/lineExample.js new file mode 100644 index 0000000000..6642e499fb --- /dev/null +++ b/examples/example/lineExample.js @@ -0,0 +1,12 @@ +Script.include("../libraries/line.js"); + +var basePosition = MyAvatar.position; +var line = new InfiniteLine(basePosition); + +for (var i = 0; i < (16 * Math.PI); i += 0.05) { + var x = 0 + var y = 0.25 * Math.sin(i); + var z = i / 10; + + line.enqueuePoint(Vec3.sum(basePosition, { x: x, y: y, z: z })); +} From 2abb6a2fd5921f474a999eba368296fb00def4b4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 16:45:58 -0700 Subject: [PATCH 172/232] Clean up line.js --- examples/libraries/line.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/libraries/line.js b/examples/libraries/line.js index 0834c6176e..991b691ae8 100644 --- a/examples/libraries/line.js +++ b/examples/libraries/line.js @@ -8,15 +8,12 @@ var MAX_LINE_LENGTH = 40; // This must be 2 or greater; var PolyLine = function(position, color, defaultStrokeWidth) { this.position = position; this.color = color; - this.defaultStrokeWidth = 0.10; + this.defaultStrokeWidth = defaultStrokeWidth; this.points = [ - { x: 0, y: 0, z: 0 } ]; this.strokeWidths = [ - this.defaultStrokeWidth ]; this.normals = [ - { x: 1, y: 0, z: 0 } ] this.entityID = Entities.addEntity({ type: "PolyLine", @@ -39,7 +36,7 @@ PolyLine.prototype.enqueuePoint = function(position) { position = Vec3.subtract(position, this.position); this.points.push(position); this.normals.push({ x: 1, y: 0, z: 0 }); - this.strokeWidths.push(this.defaultStrokeWidth * Math.min(1.0, this.points.length / 10)); + this.strokeWidths.push(this.defaultStrokeWidth); Entities.editEntity(this.entityID, { linePoints: this.points, normals: this.normals, @@ -91,7 +88,7 @@ PolyLine.prototype.destroy = function() { InfiniteLine = function(position, color) { this.position = position; this.color = color; - this.lines = [new PolyLine(position, color)]; + this.lines = [new PolyLine(position, color, 0.01)]; this.size = 0; }; @@ -99,14 +96,15 @@ InfiniteLine.prototype.enqueuePoint = function(position) { var currentLine; if (this.lines.length == 0) { - currentLine = new PolyLine(position, this.color); + currentLine = new PolyLine(position, this.color, 0.01); this.lines.push(currentLine); } else { currentLine = this.lines[this.lines.length - 1]; } if (currentLine.isFull()) { - var newLine = new PolyLine(currentLine.getLastPoint(), this.color); + var newLine = new PolyLine(currentLine.getLastPoint(), this.color, 0.01); + newLine.enqueuePoint(currentLine.getLastPoint()); this.lines.push(newLine); currentLine = newLine; } From 5bce95189b5a0716185d8a134f00ad468e7b66dc Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Mon, 26 Oct 2015 18:14:57 -0700 Subject: [PATCH 173/232] fix keyboard mapping (keyboardMouse.js) --- .../resources/controllers/keyboardMouse.json | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 7b9a8d733a..6ea1c42b1e 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -1,28 +1,53 @@ { "name": "Keyboard/Mouse to Actions", "channels": [ - { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, + { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, + { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, + { "from": "Keyboard.Left", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" } + { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" }, + + { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.TouchpadLeft", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.TouchpadRight", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, + + { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, + { "from": "Keyboard.R", "to": "Actions.ACTION1" }, + { "from": "Keyboard.T", "to": "Actions.ACTION2" } ] } From 983c551ce35c0ff5c153d31fa4294a89f38d422c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 27 Oct 2015 09:13:16 -0700 Subject: [PATCH 174/232] Clean up line.js and make lifetime settable --- examples/example/lineExample.js | 7 +++++-- examples/libraries/line.js | 27 +++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/examples/example/lineExample.js b/examples/example/lineExample.js index 6642e499fb..e6c3d90cba 100644 --- a/examples/example/lineExample.js +++ b/examples/example/lineExample.js @@ -1,12 +1,15 @@ Script.include("../libraries/line.js"); var basePosition = MyAvatar.position; -var line = new InfiniteLine(basePosition); +var color = { red: 128, green: 220, blue: 190 }; +var strokeWidth = 0.01; +var line = new InfiniteLine(basePosition, color, 20); for (var i = 0; i < (16 * Math.PI); i += 0.05) { var x = 0 var y = 0.25 * Math.sin(i); var z = i / 10; - line.enqueuePoint(Vec3.sum(basePosition, { x: x, y: y, z: z })); + var position = Vec3.sum(basePosition, { x: x, y: y, z: z }); + line.enqueuePoint(position, strokeWidth); } diff --git a/examples/libraries/line.js b/examples/libraries/line.js index 991b691ae8..77cb13c124 100644 --- a/examples/libraries/line.js +++ b/examples/libraries/line.js @@ -5,10 +5,13 @@ function error(message) { // PolyLine var LINE_DIMENSIONS = { x: 2000, y: 2000, z: 2000 }; var MAX_LINE_LENGTH = 40; // This must be 2 or greater; -var PolyLine = function(position, color, defaultStrokeWidth) { +var DEFAULT_STROKE_WIDTH = 0.1; +var DEFAULT_LIFETIME = 20; +var DEFAULT_COLOR = { red: 255, green: 255, blue: 255 }; +var PolyLine = function(position, color, lifetime) { this.position = position; this.color = color; - this.defaultStrokeWidth = defaultStrokeWidth; + this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; this.points = [ ]; this.strokeWidths = [ @@ -23,11 +26,11 @@ var PolyLine = function(position, color, defaultStrokeWidth) { strokeWidths: this.strokeWidths, dimensions: LINE_DIMENSIONS, color: color, - lifetime: 20 + lifetime: lifetime }); }; -PolyLine.prototype.enqueuePoint = function(position) { +PolyLine.prototype.enqueuePoint = function(position, strokeWidth) { if (this.isFull()) { error("Hit max PolyLine size"); return; @@ -36,7 +39,7 @@ PolyLine.prototype.enqueuePoint = function(position) { position = Vec3.subtract(position, this.position); this.points.push(position); this.normals.push({ x: 1, y: 0, z: 0 }); - this.strokeWidths.push(this.defaultStrokeWidth); + this.strokeWidths.push(strokeWidth); Entities.editEntity(this.entityID, { linePoints: this.points, normals: this.normals, @@ -83,33 +86,33 @@ PolyLine.prototype.destroy = function() { }; - // InfiniteLine -InfiniteLine = function(position, color) { +InfiniteLine = function(position, color, lifetime) { this.position = position; this.color = color; + this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; this.lines = [new PolyLine(position, color, 0.01)]; this.size = 0; }; -InfiniteLine.prototype.enqueuePoint = function(position) { +InfiniteLine.prototype.enqueuePoint = function(position, strokeWidth) { var currentLine; if (this.lines.length == 0) { - currentLine = new PolyLine(position, this.color, 0.01); + currentLine = new PolyLine(position, this.color, this.lifetime); this.lines.push(currentLine); } else { currentLine = this.lines[this.lines.length - 1]; } if (currentLine.isFull()) { - var newLine = new PolyLine(currentLine.getLastPoint(), this.color, 0.01); - newLine.enqueuePoint(currentLine.getLastPoint()); + var newLine = new PolyLine(currentLine.getLastPoint(), this.color, this.lifetime); + newLine.enqueuePoint(currentLine.getLastPoint(), strokeWidth); this.lines.push(newLine); currentLine = newLine; } - currentLine.enqueuePoint(position); + currentLine.enqueuePoint(position, strokeWidth); ++this.size; }; From 2cd2af2b13421341224910654921e9413bccd6f6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 27 Oct 2015 09:49:52 -0700 Subject: [PATCH 175/232] Add headers to line.js and lineExample.js --- examples/example/lineExample.js | 11 +++++++++++ examples/libraries/line.js | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/examples/example/lineExample.js b/examples/example/lineExample.js index e6c3d90cba..d424d4f9f3 100644 --- a/examples/example/lineExample.js +++ b/examples/example/lineExample.js @@ -1,3 +1,14 @@ +// +// lineExample.js +// examples/example +// +// Created by Ryan Huffman on October 27, 2015 +// 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 +// + Script.include("../libraries/line.js"); var basePosition = MyAvatar.position; diff --git a/examples/libraries/line.js b/examples/libraries/line.js index 77cb13c124..d31a34867b 100644 --- a/examples/libraries/line.js +++ b/examples/libraries/line.js @@ -1,3 +1,14 @@ +// +// line.js +// examples/libraries +// +// Created by Ryan Huffman on October 27, 2015 +// 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 +// + function error(message) { print("[ERROR] " + message); } From 87cf3b237b37bd91ae638cf922693b1479ae5394 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 27 Oct 2015 09:59:22 -0700 Subject: [PATCH 176/232] Supporting a InHMD action and using it to enable COmfort mode --- interface/resources/controllers/standard.json | 1 + interface/src/Application.cpp | 8 ++++++++ .../controllers/src/controllers/Actions.cpp | 2 ++ .../controllers/src/controllers/Actions.h | 2 ++ .../src/controllers/UserInputMapper.cpp | 18 ++++++++++++------ .../src/controllers/UserInputMapper.h | 2 ++ 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5d6e8ce32f..20177bfc5e 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,6 +3,7 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, + { "from": "Standard.RX", "with": "Actions.InHMD", "to": "Actions.StepYaw" }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 92680ed3e0..c1257e1279 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2703,6 +2703,9 @@ void Application::update(float deltaTime) { auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); + // Reflect some state into the Actions of the UserInpuMapper + userInputMapper->resetActionState(controller::Action::IN_HMD, (float)qApp->getAvatarUpdater()->isHMDMode()); + userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); userInputMapper->update(deltaTime); @@ -2738,6 +2741,11 @@ void Application::update(float deltaTime) { } myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } + + float lhc = userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK); + if (lhc != 0.0f) { + std::cout << "Left Hand click = " << lhc << std::endl; + } controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index a9bd32b1d8..979c8a70c1 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -75,6 +75,8 @@ namespace controller { makeAxisPair(Action::BOOM_IN, "BoomIn"), makeAxisPair(Action::BOOM_OUT, "BoomOut"), + makeButtonPair(Action::IN_HMD, "InHMD"), + // Deprecated aliases // FIXME remove after we port all scripts makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 47f04141f3..187fcd46a0 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -78,6 +78,8 @@ enum class Action { BOOM_IN, BOOM_OUT, + IN_HMD, // THis is a read only action ? + NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 2579c7dbec..3d957cbf35 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -953,19 +953,17 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error); // check validity of the document if (doc.isNull()) { + qDebug() << "Invalid JSON...\n"; + qDebug() << error.errorString(); + qDebug() << "JSON was:\n" << json << endl; return Mapping::Pointer(); } if (!doc.isObject()) { qWarning() << "Mapping json Document is not an object" << endl; + qDebug() << "JSON was:\n" << json << endl; return Mapping::Pointer(); } - - // FIXME how did we detect this? - // qDebug() << "Invalid JSON...\n"; - // qDebug() << error.errorString(); - // qDebug() << "JSON was:\n" << json << endl; - //} return parseMapping(doc.object()); } @@ -1019,5 +1017,13 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { } } +void UserInputMapper::resetActionState(Action action, float value) { + auto endpoint = endpointFor(inputFromAction(action)); + if (endpoint) { + endpoint->apply(value, 0.0f, Endpoint::Pointer()); + } + _actionStates[toInt(action)] = value; +} + } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 0a6ed3acad..40fe26aff3 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -84,7 +84,9 @@ namespace controller { Pose getPoseState(Action action) const { return _poseStates[toInt(action)]; } int findAction(const QString& actionName) const; QVector getActionNames() const; + Input inputFromAction(Action action) const { return getActionInputs()[toInt(action)].first; } + void resetActionState(Action action, float value); void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; } void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; } void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } From 596b315286bdf2de3ce0d627756f5c0a468a2d95 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Tue, 27 Oct 2015 11:35:30 -0700 Subject: [PATCH 177/232] Fixed standard.json --- interface/resources/controllers/standard.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5d6e8ce32f..8ba9056076 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -6,7 +6,7 @@ { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, - { "from": [ "Standard.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, + { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, From 5a42991e0ccd075100c2da985f7d67925fd076c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 12:30:34 -0700 Subject: [PATCH 178/232] first pass at cleaning up MyAvatars use of PalmData --- interface/src/Application.cpp | 165 ++++++++++-------- interface/src/Application.h | 15 +- interface/src/avatar/Avatar.cpp | 32 ---- interface/src/avatar/Avatar.h | 6 +- interface/src/avatar/Hand.cpp | 18 +- interface/src/avatar/MyAvatar.cpp | 76 +++----- interface/src/avatar/MyAvatar.h | 4 +- interface/src/avatar/SkeletonModel.cpp | 46 +++-- interface/src/avatar/SkeletonModel.h | 2 +- .../ControllerScriptingInterface.cpp | 45 ----- .../scripting/ControllerScriptingInterface.h | 5 - interface/src/ui/ApplicationCompositor.cpp | 15 +- libraries/avatars/src/HandData.cpp | 33 ++-- libraries/avatars/src/HandData.h | 73 +++----- 14 files changed, 213 insertions(+), 322 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 92680ed3e0..910cbb19e2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2741,13 +2741,13 @@ void Application::update(float deltaTime) { controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); - setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK)); - setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK)); + setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK)); + setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK)); if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK), - userInputMapper->getActionState(controller::Action::SHIFT), LEFT_HAND_INDEX); + userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand); emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK), - userInputMapper->getActionState(controller::Action::SHIFT), RIGHT_HAND_INDEX); + userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand); } updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... @@ -4805,86 +4805,101 @@ mat4 Application::getHMDSensorPose() const { return mat4(); } -void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, int index, float triggerValue) { - PalmData* palm; - bool foundHand = false; - for (size_t j = 0; j < hand->getNumPalms(); j++) { - if (hand->getPalms()[j].getSixenseID() == index) { - palm = &(hand->getPalms()[j]); - foundHand = true; - break; +void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) { + + // NOTE: the Hand::modifyPalms() will allow the lambda to modify the palm data while ensuring some other user isn't + // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more + // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about + // controller::Pose. More work is needed to clean this up. + hand->modifyPalms([&](std::vector& palms) { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + PalmData* palm; + bool foundHand = false; + + // FIXME - this little chuck of code is a hot mess. It's basically searching the palms + // for the one that matches the "sixenseID" that is the "index" parameter. If it + // doesn't find it, then it creates a new palm and sets that palm's ID this really + // can and should be inside the HandData class + for (size_t j = 0; j < palms.size(); j++) { + if (palms[j].whichHand() == whichHand) { + palm = &(palms[j]); + foundHand = true; + break; + } + } + if (!foundHand) { + PalmData newPalm(hand); + palms.push_back(newPalm); + palm = &(palms[palms.size() - 1]); // FIXME - lame + palm->setHand(whichHand); } - } - if (!foundHand) { - PalmData newPalm(hand); - hand->getPalms().push_back(newPalm); - palm = &(hand->getPalms()[hand->getNumPalms() - 1]); - palm->setSixenseID(index); - } - palm->setActive(pose.isValid()); + palm->setActive(pose.isValid()); - // transform from sensor space, to world space, to avatar model space. - glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); - glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix(); - glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition()); - glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; + // transform from sensor space, to world space, to avatar model space. + glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); + glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix(); + glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); + glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; - glm::vec3 position = extractTranslation(objectPose); - glm::quat rotation = glm::quat_cast(objectPose); + glm::vec3 position = extractTranslation(objectPose); + glm::quat rotation = glm::quat_cast(objectPose); - // Compute current velocity from position change - glm::vec3 rawVelocity; - if (deltaTime > 0.0f) { - rawVelocity = (position - palm->getRawPosition()) / deltaTime; - } else { - rawVelocity = glm::vec3(0.0f); - } - palm->setRawVelocity(rawVelocity); // meters/sec + // Compute current velocity from position change + glm::vec3 rawVelocity; + if (deltaTime > 0.0f) { + rawVelocity = (position - palm->getRawPosition()) / deltaTime; + } else { + rawVelocity = glm::vec3(0.0f); + } + palm->setRawVelocity(rawVelocity); // meters/sec - // Angular Velocity of Palm - glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); - glm::vec3 angularVelocity(0.0f); - float rotationAngle = glm::angle(deltaRotation); - if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { - angularVelocity = glm::normalize(glm::axis(deltaRotation)); - angularVelocity *= (rotationAngle / deltaTime); - palm->setRawAngularVelocity(angularVelocity); - } else { - palm->setRawAngularVelocity(glm::vec3(0.0f)); - } + // Angular Velocity of Palm + glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); + glm::vec3 angularVelocity(0.0f); + float rotationAngle = glm::angle(deltaRotation); + if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { + angularVelocity = glm::normalize(glm::axis(deltaRotation)); + angularVelocity *= (rotationAngle / deltaTime); + palm->setRawAngularVelocity(angularVelocity); + } else { + palm->setRawAngularVelocity(glm::vec3(0.0f)); + } - if (controller::InputDevice::getLowVelocityFilter()) { - // Use a velocity sensitive filter to damp small motions and preserve large ones with - // no latency. - float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); - position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); - rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter); - } - palm->setRawPosition(position); - palm->setRawRotation(rotation); + if (controller::InputDevice::getLowVelocityFilter()) { + // Use a velocity sensitive filter to damp small motions and preserve large ones with + // no latency. + float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); + position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); + rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter); + } + palm->setRawPosition(position); + palm->setRawRotation(rotation); - // Store the one fingertip in the palm structure so we can track velocity - const float FINGER_LENGTH = 0.3f; // meters - const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); - const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; - glm::vec3 oldTipPosition = palm->getTipRawPosition(); - if (deltaTime > 0.0f) { - palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); - } else { - palm->setTipVelocity(glm::vec3(0.0f)); - } - palm->setTipPosition(newTipPosition); - palm->setTrigger(triggerValue); + // Store the one fingertip in the palm structure so we can track velocity + const float FINGER_LENGTH = 0.3f; // meters + const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); + const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; + glm::vec3 oldTipPosition = palm->getTipRawPosition(); + if (deltaTime > 0.0f) { + palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); + } else { + palm->setTipVelocity(glm::vec3(0.0f)); + } + palm->setTipPosition(newTipPosition); + palm->setTrigger(triggerValue); + }); } -void Application::emulateMouse(Hand* hand, float click, float shift, int index) { +void Application::emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand) { + auto palms = hand->getCopyOfPalms(); + // Locate the palm, if it exists and is active PalmData* palm; bool foundHand = false; - for (size_t j = 0; j < hand->getNumPalms(); j++) { - if (hand->getPalms()[j].getSixenseID() == index) { - palm = &(hand->getPalms()[j]); + for (size_t j = 0; j < palms.size(); j++) { + if (palms[j].whichHand() == whichHand) { + palm = &(palms[j]); foundHand = true; break; } @@ -4896,12 +4911,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) // Process the mouse events QPoint pos; - unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; + + // FIXME - this mouse emulation stuff needs to be reworked for new controller input plugins + unsigned int deviceID = whichHand == HandData::LeftHand ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; + int index = (int)whichHand; // FIXME - hack attack if (isHMDMode()) { pos = getApplicationCompositor().getPalmClickLocation(palm); - } - else { + } else { // Get directon relative to avatar orientation glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 75cc418e94..301eb3b262 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -352,8 +352,8 @@ private: void update(float deltaTime); - void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, int index, float triggerValue); - void emulateMouse(Hand* hand, float click, float shift, int index); + void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue); + void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand); // Various helper functions called during update() void updateLOD(); @@ -522,10 +522,13 @@ private: ApplicationCompositor _compositor; OverlayConductor _overlayConductor; - int _oldHandMouseX[2]; - int _oldHandMouseY[2]; - bool _oldHandLeftClick[2]; - bool _oldHandRightClick[2]; + + // FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved + // into the input plugins or something. + int _oldHandMouseX[(int)HandData::NUMBER_OF_HANDS]; + int _oldHandMouseY[(int)HandData::NUMBER_OF_HANDS]; + bool _oldHandLeftClick[(int)HandData::NUMBER_OF_HANDS]; + bool _oldHandRightClick[(int)HandData::NUMBER_OF_HANDS]; DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9f4e7ee3cf..8c29ce1a41 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1177,22 +1177,6 @@ glm::vec3 Avatar::getLeftPalmPosition() { return leftHandPosition; } -glm::vec3 Avatar::getLeftPalmVelocity() { - const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX); - if (palm != NULL) { - return palm->getVelocity(); - } - return glm::vec3(0.0f); -} - -glm::vec3 Avatar::getLeftPalmAngularVelocity() { - const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX); - if (palm != NULL) { - return palm->getRawAngularVelocity(); - } - return glm::vec3(0.0f); -} - glm::quat Avatar::getLeftPalmRotation() { glm::quat leftRotation; getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); @@ -1208,22 +1192,6 @@ glm::vec3 Avatar::getRightPalmPosition() { return rightHandPosition; } -glm::vec3 Avatar::getRightPalmVelocity() { - const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX); - if (palm != NULL) { - return palm->getVelocity(); - } - return glm::vec3(0.0f); -} - -glm::vec3 Avatar::getRightPalmAngularVelocity() { - const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX); - if (palm != NULL) { - return palm->getRawAngularVelocity(); - } - return glm::vec3(0.0f); -} - glm::quat Avatar::getRightPalmRotation() { glm::quat rightRotation; getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 6a1f216089..44b5d91015 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -160,13 +160,11 @@ public: AvatarMotionState* getMotionState() { return _motionState; } public slots: + + // FIXME - these should be migrated to use Pose data instead glm::vec3 getLeftPalmPosition(); - glm::vec3 getLeftPalmVelocity(); - glm::vec3 getLeftPalmAngularVelocity(); glm::quat getLeftPalmRotation(); glm::vec3 getRightPalmPosition(); - glm::vec3 getRightPalmVelocity(); - glm::vec3 getRightPalmAngularVelocity(); glm::quat getRightPalmRotation(); protected: diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 0eeb7222b6..dfac5e393f 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -31,13 +31,7 @@ Hand::Hand(Avatar* owningAvatar) : } void Hand::simulate(float deltaTime, bool isMine) { - if (isMine) { - // Iterate hand controllers, take actions as needed - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; - palm.setLastControllerButtons(palm.getControllerButtons()); - } - } + // nothing to do here } void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { @@ -53,10 +47,12 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { const glm::vec3 grayColor(0.5f); const float SPHERE_RADIUS = 0.03f * avatarScale; + auto palms = getCopyOfPalms(); + gpu::Batch& batch = *renderArgs->_batch; if (isMine) { - for (size_t i = 0; i < getNumPalms(); i++) { - PalmData& palm = getPalms()[i]; + for (size_t i = 0; i < palms.size(); i++) { + PalmData& palm = palms[i]; if (!palm.isActive()) { continue; } @@ -82,8 +78,8 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS; // Draw the coordinate frames of the hand targets - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; + for (size_t i = 0; i < palms.size(); ++i) { + PalmData& palm = palms[i]; if (palm.isActive()) { glm::vec3 root = palm.getPosition(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6e08ca24cf..5483219318 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -554,74 +554,76 @@ void MyAvatar::updateFromTrackers(float deltaTime) { // first active controller. If you have both controllers held up or just the left, that // will be correct. But if you lift the right controller, then it will be reported // as "left"... you also see this in the avatars hands. -const PalmData* MyAvatar::getActivePalm(int palmIndex) const { - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - int numberOfPalms = handData->getNumPalms(); +PalmData MyAvatar::getActivePalmData(int palmIndex) const { + auto palms = getHandData()->getCopyOfPalms(); + + int numberOfPalms = palms.size(); int numberOfActivePalms = 0; for (int i = 0; i < numberOfPalms; i++) { - auto palm = handData->getPalms()[i]; + auto palm = palms[i]; if (palm.isActive()) { // if we've reached the requested "active" palm, then we will return it if (numberOfActivePalms == palmIndex) { - return &handData->getPalms()[i]; + return palm; } numberOfActivePalms++; } } - return NULL; + PalmData noData(nullptr); + return noData; } glm::vec3 MyAvatar::getLeftHandPosition() const { const int LEFT_HAND = 0; - auto palmData = getActivePalm(LEFT_HAND); - return palmData ? palmData->getPosition() : glm::vec3(0.0f); + auto palmData = getActivePalmData(LEFT_HAND); + return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandPosition() const { const int RIGHT_HAND = 1; - auto palmData = getActivePalm(RIGHT_HAND); - return palmData ? palmData->getPosition() : glm::vec3(0.0f); + auto palmData = getActivePalmData(RIGHT_HAND); + return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getLeftHandTipPosition() const { const int LEFT_HAND = 0; - auto palmData = getActivePalm(LEFT_HAND); - return palmData ? palmData->getTipPosition() : glm::vec3(0.0f); + auto palmData = getActivePalmData(LEFT_HAND); + return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandTipPosition() const { const int RIGHT_HAND = 1; - auto palmData = getActivePalm(RIGHT_HAND); - return palmData ? palmData->getTipPosition() : glm::vec3(0.0f); + auto palmData = getActivePalmData(RIGHT_HAND); + return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f); } controller::Pose MyAvatar::getLeftHandPose() const { const int LEFT_HAND = 0; - auto palmData = getActivePalm(LEFT_HAND); - return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), - palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + auto palmData = getActivePalmData(LEFT_HAND); + return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), + palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandPose() const { const int RIGHT_HAND = 1; - auto palmData = getActivePalm(RIGHT_HAND); - return palmData ? controller::Pose(palmData->getPosition(), palmData->getRotation(), - palmData->getVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + auto palmData = getActivePalmData(RIGHT_HAND); + return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), + palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getLeftHandTipPose() const { const int LEFT_HAND = 0; - auto palmData = getActivePalm(LEFT_HAND); - return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), - palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + auto palmData = getActivePalmData(LEFT_HAND); + return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), + palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandTipPose() const { const int RIGHT_HAND = 1; - auto palmData = getActivePalm(RIGHT_HAND); - return palmData ? controller::Pose(palmData->getTipPosition(), palmData->getRotation(), - palmData->getTipVelocity(), palmData->getRawAngularVelocityAsQuat()) : controller::Pose(); + auto palmData = getActivePalmData(RIGHT_HAND); + return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), + palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } // virtual @@ -1967,28 +1969,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() { _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); } -//Renders sixense laser pointers for UI selection with controllers -void MyAvatar::renderLaserPointers(gpu::Batch& batch) { - const float PALM_TIP_ROD_RADIUS = 0.002f; - - //If the Oculus is enabled, we will draw a blue cursor ray - - for (size_t i = 0; i < getHand()->getNumPalms(); ++i) { - PalmData& palm = getHand()->getPalms()[i]; - if (palm.isActive()) { - glm::vec3 tip = getLaserPointerTipPosition(&palm); - glm::vec3 root = palm.getPosition(); - - //Scale the root vector with the avatar scale - scaleVectorRelativeToPosition(root); - Transform transform = Transform(); - transform.setTranslation(glm::vec3()); - batch.setModelTransform(transform); - Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1)); - } - } -} - //Gets the tip position for the laser pointer glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0a3d7dedf4..b6cce7165a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -294,7 +294,6 @@ private: const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, bool allowDuplicates = false, bool useSaved = true) override; - void renderLaserPointers(gpu::Batch& batch); const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } @@ -310,8 +309,7 @@ private: void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); - const PalmData* getActivePalm(int palmIndex) const; - + PalmData getActivePalmData(int palmIndex) const; // derive avatar body position and orientation from the current HMD Sensor location. // results are in sensor space diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 28c7941c52..f821c79d59 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -97,17 +97,6 @@ void SkeletonModel::initJointStates(QVector states) { emit skeletonLoaded(); } -static const PalmData* getPalmWithIndex(Hand* hand, int index) { - const PalmData* palm = nullptr; - for (size_t j = 0; j < hand->getNumPalms(); j++) { - if (hand->getPalms()[j].getSixenseID() == index) { - palm = &(hand->getPalms()[j]); - break; - } - } - return palm; -} - const float PALM_PRIORITY = DEFAULT_PRIORITY; // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { @@ -169,22 +158,22 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::HandParameters handParams; - const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX); - if (leftPalm && leftPalm->isActive()) { + auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand); + if (leftPalm.isValid() && leftPalm.isActive()) { handParams.isLeftEnabled = true; - handParams.leftPosition = leftPalm->getRawPosition(); - handParams.leftOrientation = leftPalm->getRawRotation(); - handParams.leftTrigger = leftPalm->getTrigger(); + handParams.leftPosition = leftPalm.getRawPosition(); + handParams.leftOrientation = leftPalm.getRawRotation(); + handParams.leftTrigger = leftPalm.getTrigger(); } else { handParams.isLeftEnabled = false; } - const PalmData* rightPalm = getPalmWithIndex(myAvatar->getHand(), RIGHT_HAND_INDEX); - if (rightPalm && rightPalm->isActive()) { + auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand); + if (rightPalm.isValid() && rightPalm.isActive()) { handParams.isRightEnabled = true; - handParams.rightPosition = rightPalm->getRawPosition(); - handParams.rightOrientation = rightPalm->getRawRotation(); - handParams.rightTrigger = rightPalm->getTrigger(); + handParams.rightPosition = rightPalm.getRawPosition(); + handParams.rightOrientation = rightPalm.getRawRotation(); + handParams.rightTrigger = rightPalm.getTrigger(); } else { handParams.isRightEnabled = false; } @@ -250,10 +239,15 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { // find the left and rightmost active palms int leftPalmIndex, rightPalmIndex; Hand* hand = _owningAvatar->getHand(); - hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); + + // FIXME - it's possible that the left/right hand indices could change between this call + // and the call to hand->getCopyOfPalms(); This logic should be reworked to only operate on + // the copy of the palms data + hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); // Don't Relax toward hand positions when in animGraph mode. if (!_rig->getEnableAnimGraph()) { + auto palms = hand->getCopyOfPalms(); const float HAND_RESTORATION_RATE = 0.25f; if (leftPalmIndex == -1 && rightPalmIndex == -1) { // palms are not yet set, use mouse @@ -268,17 +262,17 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } else if (leftPalmIndex == rightPalmIndex) { // right hand only - applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, palms[leftPalmIndex]); restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } else { if (leftPalmIndex != -1) { - applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.leftHandJointIndex, palms[leftPalmIndex]); } else { restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } if (rightPalmIndex != -1) { - applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, palms[rightPalmIndex]); } else { restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } @@ -329,7 +323,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) PALM_PRIORITY); } -void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { +void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index d655d6e01f..dc08168a8c 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -118,7 +118,7 @@ protected: /// \param position position of joint in model-frame void applyHandPosition(int jointIndex, const glm::vec3& position); - void applyPalmData(int jointIndex, PalmData& palm); + void applyPalmData(int jointIndex, const PalmData& palm); private: void renderJointConstraints(gpu::Batch& batch, int jointIndex); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 31f823de8b..547f16ea8b 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -35,51 +35,6 @@ void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { } } -const PalmData* ControllerScriptingInterface::getPrimaryPalm() const { - int leftPalmIndex, rightPalmIndex; - - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - handData->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - - if (rightPalmIndex != -1) { - return &handData->getPalms()[rightPalmIndex]; - } - - return NULL; -} - -int ControllerScriptingInterface::getNumberOfActivePalms() const { - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - int numberOfPalms = handData->getNumPalms(); - int numberOfActivePalms = 0; - for (int i = 0; i < numberOfPalms; i++) { - if (getPalm(i)->isActive()) { - numberOfActivePalms++; - } - } - return numberOfActivePalms; -} - -const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const { - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - return &handData->getPalms()[palmIndex]; -} - -const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const { - const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); - int numberOfPalms = handData->getNumPalms(); - int numberOfActivePalms = 0; - for (int i = 0; i < numberOfPalms; i++) { - if (getPalm(i)->isActive()) { - if (numberOfActivePalms == palmIndex) { - return &handData->getPalms()[i]; - } - numberOfActivePalms++; - } - } - return NULL; -} - bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { return isKeyCaptured(KeyEvent(*event)); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 3133f93804..4c69551dd2 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -123,11 +123,6 @@ signals: private: QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript - const PalmData* getPrimaryPalm() const; - const PalmData* getPalm(int palmIndex) const; - int getNumberOfActivePalms() const; - const PalmData* getActivePalm(int palmIndex) const; - QMultiMap _capturedKeys; QSet _capturedJoysticks; diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index ca5c60dc04..2f442a3284 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -320,8 +320,9 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int // Only render the hand pointers if the EnableHandMouseInput is enabled if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { - PalmData& palm = myAvatar->getHand()->getPalms()[i]; + auto palms = myAvatar->getHand()->getCopyOfPalms(); + for (int i = 0; i < (int)palms.size(); i++) { + const auto& palm = palms[i]; if (palm.isActive()) { glm::vec2 polar = getPolarCoordinates(palm); // Convert to quaternion @@ -446,6 +447,7 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) { } +// FIXME - this is old code that likely needs to be removed and/or reworked to support the new input control model void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); @@ -455,23 +457,24 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; const HandData* handData = DependencyManager::get()->getMyAvatar()->getHandData(); + auto palms = handData->getCopyOfPalms(); for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { const int index = palmIndex - 1; const PalmData* palmData = NULL; - if (palmIndex >= handData->getPalms().size()) { + if (palmIndex >= palms.size()) { return; } - if (handData->getPalms()[palmIndex].isActive()) { - palmData = &handData->getPalms()[palmIndex]; + if (palms[palmIndex].isActive()) { + palmData = &palms[palmIndex]; } else { continue; } - int controllerButtons = palmData->getControllerButtons(); + int controllerButtons = 0; //Check for if we should toggle or drag the magnification window if (controllerButtons & BUTTON_3) { diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 1a9b6775d3..4e08f5190f 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -21,6 +21,8 @@ HandData::HandData(AvatarData* owningAvatar) : _owningAvatarData(owningAvatar) { + // FIXME - this is likely the source of the fact that with Hydras and other input plugins with hand controllers + // we end up with 4 palms... because we end up adding palms once we know the SixenseIDs // Start with two palms addNewPalm(); addNewPalm(); @@ -31,32 +33,35 @@ glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { } PalmData& HandData::addNewPalm() { + QWriteLocker locker(&_palmsLock); _palms.push_back(PalmData(this)); return _palms.back(); } -const PalmData* HandData::getPalm(int sixSenseID) const { +PalmData HandData::getCopyOfPalmData(Hand hand) const { + QReadLocker locker(&_palmsLock); + // the palms are not necessarily added in left-right order, - // so we have to search for the right SixSenseID - for (unsigned int i = 0; i < _palms.size(); i++) { - const PalmData* palm = &(_palms[i]); - if (palm->getSixenseID() == sixSenseID) { - return palm->isActive() ? palm : NULL; + // so we have to search for the correct hand + for (const auto& palm : _palms) { + if (palm.whichHand() == hand && palm.isActive()) { + return palm; } } - return NULL; + return PalmData(nullptr); // invalid hand } void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { + QReadLocker locker(&_palmsLock); leftPalmIndex = -1; rightPalmIndex = -1; for (size_t i = 0; i < _palms.size(); i++) { const PalmData& palm = _palms[i]; if (palm.isActive()) { - if (palm.getSixenseID() == LEFT_HAND_INDEX) { + if (palm.whichHand() == LeftHand) { leftPalmIndex = i; } - if (palm.getSixenseID() == RIGHT_HAND_INDEX) { + if (palm.whichHand() == RightHand) { rightPalmIndex = i; } } @@ -69,14 +74,9 @@ _rawPosition(0.0f), _rawVelocity(0.0f), _rawAngularVelocity(0.0f), _totalPenetration(0.0f), -_controllerButtons(0), _isActive(false), -_sixenseID(SIXENSEID_INVALID), _numFramesWithoutData(0), -_owningHandData(owningHandData), -_isCollidingWithVoxel(false), -_isCollidingWithPalm(false), -_collisionlessPaddleExpiry(0) { +_owningHandData(owningHandData) { } void PalmData::addToPosition(const glm::vec3& delta) { @@ -85,7 +85,8 @@ void PalmData::addToPosition(const glm::vec3& delta) { bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, const PalmData*& collidingPalm) const { - + QReadLocker locker(&_palmsLock); + for (size_t i = 0; i < _palms.size(); ++i) { const PalmData& palm = _palms[i]; if (!palm.isActive()) { diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 855da63870..ca2aa9836a 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -12,26 +12,30 @@ #ifndef hifi_HandData_h #define hifi_HandData_h +#include #include #include #include #include +#include + #include #include class AvatarData; class PalmData; -const int LEFT_HAND_INDEX = 0; -const int RIGHT_HAND_INDEX = 1; -const int NUM_HANDS = 2; - -const int SIXENSEID_INVALID = -1; - class HandData { public: + enum Hand { + UnknownHand, + RightHand, + LeftHand, + NUMBER_OF_HANDS + }; + HandData(AvatarData* owningAvatar); virtual ~HandData() {} @@ -46,11 +50,10 @@ public: glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const; - std::vector& getPalms() { return _palms; } - const std::vector& getPalms() const { return _palms; } - const PalmData* getPalm(int sixSenseID) const; - size_t getNumPalms() const { return _palms.size(); } + PalmData getCopyOfPalmData(Hand hand) const; + PalmData& addNewPalm(); + std::vector getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; } /// Finds the indices of the left and right palms according to their locations, or -1 if either or /// both is not found. @@ -67,10 +70,15 @@ public: glm::quat getBaseOrientation() const; + /// Allows a lamda function write access to the palms for this Hand + void modifyPalms(std::function& palms)> callback) + { QWriteLocker locker(&_palmsLock); callback(_palms);} + friend class AvatarData; protected: AvatarData* _owningAvatarData; std::vector _palms; + mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive }; glm::vec3 getBasePosition() const; float getBaseScale() const; @@ -90,10 +98,12 @@ public: const glm::vec3& getRawPosition() const { return _rawPosition; } bool isActive() const { return _isActive; } - int getSixenseID() const { return _sixenseID; } + bool isValid() const { return _owningHandData; } void setActive(bool active) { _isActive = active; } - void setSixenseID(int id) { _sixenseID = id; } + + HandData::Hand whichHand() const { return _hand; } + void setHand(HandData::Hand hand) { _hand = hand; } void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; }; glm::quat getRawRotation() const { return _rawRotation; } @@ -123,31 +133,11 @@ public: void resetFramesWithoutData() { _numFramesWithoutData = 0; } int getFramesWithoutData() const { return _numFramesWithoutData; } - // Controller buttons - void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; } - void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; } - - unsigned int getControllerButtons() const { return _controllerButtons; } - unsigned int getLastControllerButtons() const { return _lastControllerButtons; } - + // FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information + // from an action and/or the UserInputMapper instead of piping it through here. void setTrigger(float trigger) { _trigger = trigger; } float getTrigger() const { return _trigger; } - void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; } - float getJoystickX() const { return _joystickX; } - float getJoystickY() const { return _joystickY; } - bool getIsCollidingWithVoxel() const { return _isCollidingWithVoxel; } - void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; } - - bool getIsCollidingWithPalm() const { return _isCollidingWithPalm; } - void setIsCollidingWithPalm(bool isCollidingWithPalm) { _isCollidingWithPalm = isCollidingWithPalm; } - - bool hasPaddle() const { return _collisionlessPaddleExpiry < usecTimestampNow(); } - void updateCollisionlessPaddleExpiry() { _collisionlessPaddleExpiry = usecTimestampNow() + USECS_PER_SECOND; } - - /// Store position where the palm holds the ball. - void getBallHoldPosition(glm::vec3& position) const; - // return world-frame: glm::vec3 getFingerDirection() const; glm::vec3 getNormal() const; @@ -163,21 +153,14 @@ private: glm::vec3 _tipPosition; glm::vec3 _tipVelocity; - glm::vec3 _totalPenetration; // accumulator for per-frame penetrations + glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations - unsigned int _controllerButtons; - unsigned int _lastControllerButtons; float _trigger; - float _joystickX, _joystickY; - bool _isActive; // This has current valid data - int _sixenseID; // Sixense controller ID for this palm - int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost. + bool _isActive; /// This has current valid data + HandData::Hand _hand = HandData::UnknownHand; + int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost. HandData* _owningHandData; - - bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel - bool _isCollidingWithPalm; - quint64 _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding }; #endif // hifi_HandData_h From 133d48ebee8c24cfd1fba989037da20b361573e7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 15:53:48 -0700 Subject: [PATCH 179/232] CR feedback --- interface/src/Application.cpp | 53 +++++++++--------------------- libraries/avatars/src/HandData.cpp | 19 +++++------ libraries/avatars/src/HandData.h | 28 +++++++++++----- 3 files changed, 44 insertions(+), 56 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 910cbb19e2..29f4bd6ece 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4811,30 +4811,9 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about // controller::Pose. More work is needed to clean this up. - hand->modifyPalms([&](std::vector& palms) { + hand->modifyPalm(whichHand, [&](PalmData& palm) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - PalmData* palm; - bool foundHand = false; - - // FIXME - this little chuck of code is a hot mess. It's basically searching the palms - // for the one that matches the "sixenseID" that is the "index" parameter. If it - // doesn't find it, then it creates a new palm and sets that palm's ID this really - // can and should be inside the HandData class - for (size_t j = 0; j < palms.size(); j++) { - if (palms[j].whichHand() == whichHand) { - palm = &(palms[j]); - foundHand = true; - break; - } - } - if (!foundHand) { - PalmData newPalm(hand); - palms.push_back(newPalm); - palm = &(palms[palms.size() - 1]); // FIXME - lame - palm->setHand(whichHand); - } - - palm->setActive(pose.isValid()); + palm.setActive(pose.isValid()); // transform from sensor space, to world space, to avatar model space. glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); @@ -4848,46 +4827,46 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de // Compute current velocity from position change glm::vec3 rawVelocity; if (deltaTime > 0.0f) { - rawVelocity = (position - palm->getRawPosition()) / deltaTime; + rawVelocity = (position - palm.getRawPosition()) / deltaTime; } else { rawVelocity = glm::vec3(0.0f); } - palm->setRawVelocity(rawVelocity); // meters/sec + palm.setRawVelocity(rawVelocity); // meters/sec // Angular Velocity of Palm - glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); + glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation()); glm::vec3 angularVelocity(0.0f); float rotationAngle = glm::angle(deltaRotation); if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { angularVelocity = glm::normalize(glm::axis(deltaRotation)); angularVelocity *= (rotationAngle / deltaTime); - palm->setRawAngularVelocity(angularVelocity); + palm.setRawAngularVelocity(angularVelocity); } else { - palm->setRawAngularVelocity(glm::vec3(0.0f)); + palm.setRawAngularVelocity(glm::vec3(0.0f)); } if (controller::InputDevice::getLowVelocityFilter()) { // Use a velocity sensitive filter to damp small motions and preserve large ones with // no latency. float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); - position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); - rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter); + position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); + rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter); } - palm->setRawPosition(position); - palm->setRawRotation(rotation); + palm.setRawPosition(position); + palm.setRawRotation(rotation); // Store the one fingertip in the palm structure so we can track velocity const float FINGER_LENGTH = 0.3f; // meters const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; - glm::vec3 oldTipPosition = palm->getTipRawPosition(); + glm::vec3 oldTipPosition = palm.getTipRawPosition(); if (deltaTime > 0.0f) { - palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); + palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); } else { - palm->setTipVelocity(glm::vec3(0.0f)); + palm.setTipVelocity(glm::vec3(0.0f)); } - palm->setTipPosition(newTipPosition); - palm->setTrigger(triggerValue); + palm.setTipPosition(newTipPosition); + palm.setTrigger(triggerValue); }); } diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 4e08f5190f..0237f8ecd9 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -21,20 +21,17 @@ HandData::HandData(AvatarData* owningAvatar) : _owningAvatarData(owningAvatar) { - // FIXME - this is likely the source of the fact that with Hydras and other input plugins with hand controllers - // we end up with 4 palms... because we end up adding palms once we know the SixenseIDs - // Start with two palms - addNewPalm(); - addNewPalm(); + addNewPalm(LeftHand); + addNewPalm(RightHand); } glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale(); } -PalmData& HandData::addNewPalm() { +PalmData& HandData::addNewPalm(Hand whichHand) { QWriteLocker locker(&_palmsLock); - _palms.push_back(PalmData(this)); + _palms.push_back(PalmData(this, whichHand)); return _palms.back(); } @@ -48,7 +45,8 @@ PalmData HandData::getCopyOfPalmData(Hand hand) const { return palm; } } - return PalmData(nullptr); // invalid hand + PalmData noData; + return noData; // invalid hand } void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { @@ -68,7 +66,7 @@ void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) } } -PalmData::PalmData(HandData* owningHandData) : +PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) : _rawRotation(0.0f, 0.0f, 0.0f, 1.0f), _rawPosition(0.0f), _rawVelocity(0.0f), @@ -76,7 +74,8 @@ _rawAngularVelocity(0.0f), _totalPenetration(0.0f), _isActive(false), _numFramesWithoutData(0), -_owningHandData(owningHandData) { +_owningHandData(owningHandData), +_hand(hand) { } void PalmData::addToPosition(const glm::vec3& delta) { diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index ca2aa9836a..10292daccc 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -30,9 +30,9 @@ class PalmData; class HandData { public: enum Hand { - UnknownHand, - RightHand, LeftHand, + RightHand, + UnknownHand, NUMBER_OF_HANDS }; @@ -52,7 +52,6 @@ public: PalmData getCopyOfPalmData(Hand hand) const; - PalmData& addNewPalm(); std::vector getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; } /// Finds the indices of the left and right palms according to their locations, or -1 if either or @@ -70,9 +69,17 @@ public: glm::quat getBaseOrientation() const; - /// Allows a lamda function write access to the palms for this Hand - void modifyPalms(std::function& palms)> callback) - { QWriteLocker locker(&_palmsLock); callback(_palms);} + /// Allows a lamda function write access to the specific palm for this Hand, this might + /// modify the _palms vector + template void modifyPalm(Hand whichHand, PalmModifierFunction callback) { + QReadLocker locker(&_palmsLock); + for (auto& palm : _palms) { + if (palm.whichHand() == whichHand && palm.isValid()) { + callback(palm); + return; + } + } + } friend class AvatarData; protected: @@ -82,7 +89,10 @@ protected: glm::vec3 getBasePosition() const; float getBaseScale() const; - + + PalmData& addNewPalm(Hand whichHand); + PalmData& getPalmData(Hand hand); + private: // privatize copy ctor and assignment operator so copies of this object cannot be made HandData(const HandData&); @@ -92,7 +102,7 @@ private: class PalmData { public: - PalmData(HandData* owningHandData); + PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand); glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); } glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); } @@ -158,7 +168,7 @@ private: float _trigger; bool _isActive; /// This has current valid data - HandData::Hand _hand = HandData::UnknownHand; + HandData::Hand _hand; int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost. HandData* _owningHandData; }; From eb1936412954d0eabf2152f9fc759b5a2c9405c0 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 15:56:27 -0700 Subject: [PATCH 180/232] CR feedback --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5483219318..1b1a2a18e0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -569,8 +569,8 @@ PalmData MyAvatar::getActivePalmData(int palmIndex) const { numberOfActivePalms++; } } - PalmData noData(nullptr); - return noData; + ; + return PalmData(); } From 19743c1f39c17743634d3b7894a57a882f8d3b55 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 16:16:03 -0700 Subject: [PATCH 181/232] gak, build busters and more CR feedback --- interface/src/Application.cpp | 4 +-- interface/src/avatar/MyAvatar.cpp | 50 +++++------------------------- libraries/avatars/src/HandData.cpp | 3 +- libraries/avatars/src/HandData.h | 24 +++++++------- 4 files changed, 24 insertions(+), 57 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 29f4bd6ece..5f5b8e68c5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4807,7 +4807,7 @@ mat4 Application::getHMDSensorPose() const { void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) { - // NOTE: the Hand::modifyPalms() will allow the lambda to modify the palm data while ensuring some other user isn't + // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about // controller::Pose. More work is needed to clean this up. @@ -4866,7 +4866,7 @@ void Application::setPalmData(Hand* hand, const controller::Pose& pose, float de palm.setTipVelocity(glm::vec3(0.0f)); } palm.setTipPosition(newTipPosition); - palm.setTrigger(triggerValue); + palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger }); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1b1a2a18e0..18a7f4eb52 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -548,80 +548,46 @@ void MyAvatar::updateFromTrackers(float deltaTime) { } -// FIXME - this is super duper dumb... but this is how master works. When you have -// hydras plugged in, you'll get 4 "palms" but only the number of controllers lifted -// of the base station are considered active. So when you ask for "left" you get the -// first active controller. If you have both controllers held up or just the left, that -// will be correct. But if you lift the right controller, then it will be reported -// as "left"... you also see this in the avatars hands. -PalmData MyAvatar::getActivePalmData(int palmIndex) const { - auto palms = getHandData()->getCopyOfPalms(); - - int numberOfPalms = palms.size(); - int numberOfActivePalms = 0; - for (int i = 0; i < numberOfPalms; i++) { - auto palm = palms[i]; - if (palm.isActive()) { - // if we've reached the requested "active" palm, then we will return it - if (numberOfActivePalms == palmIndex) { - return palm; - } - numberOfActivePalms++; - } - } - ; - return PalmData(); -} - - glm::vec3 MyAvatar::getLeftHandPosition() const { - const int LEFT_HAND = 0; - auto palmData = getActivePalmData(LEFT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandPosition() const { - const int RIGHT_HAND = 1; - auto palmData = getActivePalmData(RIGHT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getLeftHandTipPosition() const { - const int LEFT_HAND = 0; - auto palmData = getActivePalmData(LEFT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f); } glm::vec3 MyAvatar::getRightHandTipPosition() const { - const int RIGHT_HAND = 1; - auto palmData = getActivePalmData(RIGHT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f); } controller::Pose MyAvatar::getLeftHandPose() const { - const int LEFT_HAND = 0; - auto palmData = getActivePalmData(LEFT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandPose() const { - const int RIGHT_HAND = 1; - auto palmData = getActivePalmData(RIGHT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getLeftHandTipPose() const { - const int LEFT_HAND = 0; - auto palmData = getActivePalmData(LEFT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } controller::Pose MyAvatar::getRightHandTipPose() const { - const int RIGHT_HAND = 1; - auto palmData = getActivePalmData(RIGHT_HAND); + auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); } diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 0237f8ecd9..413a5ea955 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -45,8 +45,7 @@ PalmData HandData::getCopyOfPalmData(Hand hand) const { return palm; } } - PalmData noData; - return noData; // invalid hand + return PalmData(); // invalid hand } void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index 10292daccc..b475248d9e 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -71,15 +71,7 @@ public: /// Allows a lamda function write access to the specific palm for this Hand, this might /// modify the _palms vector - template void modifyPalm(Hand whichHand, PalmModifierFunction callback) { - QReadLocker locker(&_palmsLock); - for (auto& palm : _palms) { - if (palm.whichHand() == whichHand && palm.isValid()) { - callback(palm); - return; - } - } - } + template void modifyPalm(Hand whichHand, PalmModifierFunction callback); friend class AvatarData; protected: @@ -115,7 +107,7 @@ public: HandData::Hand whichHand() const { return _hand; } void setHand(HandData::Hand hand) { _hand = hand; } - void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; }; + void setRawRotation(const glm::quat& rawRotation) { _rawRotation = rawRotation; }; glm::quat getRawRotation() const { return _rawRotation; } glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; } void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } @@ -168,9 +160,19 @@ private: float _trigger; bool _isActive; /// This has current valid data - HandData::Hand _hand; int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost. HandData* _owningHandData; + HandData::Hand _hand; }; +template void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) { + QReadLocker locker(&_palmsLock); + for (auto& palm : _palms) { + if (palm.whichHand() == whichHand && palm.isValid()) { + callback(palm); + return; + } + } +} + #endif // hifi_HandData_h From b8f189dea752744e1e9ef58c0f42edb602b19a47 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 27 Oct 2015 16:25:29 -0700 Subject: [PATCH 182/232] Fixing hydras --- .../src/input-plugins/SixenseManager.cpp | 52 ++++++++++--------- .../src/input-plugins/SixenseManager.h | 9 +--- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 008645abfe..d5ff4c93a8 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -67,11 +67,7 @@ const float DEFAULT_REACH_LENGTH = 1.5f; SixenseManager::SixenseManager() : InputDevice("Hydra"), - _reachLength(DEFAULT_REACH_LENGTH), -#ifdef __APPLE__ - _sixenseLibrary(nullptr), -#endif - _hydrasConnected(false) + _reachLength(DEFAULT_REACH_LENGTH) { } @@ -94,6 +90,9 @@ void SixenseManager::activate() { [this] (bool clicked) { this->setSixenseFilter(clicked); }, true, true); + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(this); + #ifdef __APPLE__ if (!_sixenseLibrary) { @@ -121,9 +120,6 @@ void SixenseManager::activate() { #endif loadSettings(); sixenseInit(); - _activated = true; - auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(this); #endif } @@ -134,13 +130,19 @@ void SixenseManager::deactivate() { CONTAINER->removeMenu(MENU_PATH); _poseStateMap.clear(); + _collectedSamples.clear(); + + if (_deviceID != controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(_deviceID); + _deviceID = controller::Input::INVALID_DEVICE; + } #ifdef __APPLE__ SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit"); #endif sixenseExit(); - _activated = false; #ifdef __APPLE__ delete _sixenseLibrary; @@ -176,30 +178,32 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { auto userInputMapper = DependencyManager::get(); + static const float MAX_DISCONNECTED_TIME = 2.0f; + static bool disconnected { false }; + static float disconnectedInterval { 0.0f }; if (sixenseGetNumActiveControllers() == 0) { - if (_hydrasConnected) { - qCDebug(inputplugins) << "hydra disconnected" << _badDataCount; - if (_badDataCount++ < _allowedBadDataCount) { // gotta get some no-active in a row before we shut things down - return; - } + if (!disconnected) { + disconnectedInterval += deltaTime; } - _hydrasConnected = false; - if (_deviceID != 0) { - userInputMapper->removeDevice(_deviceID); - _deviceID = 0; + if (disconnectedInterval > MAX_DISCONNECTED_TIME) { + disconnected = true; + _axisStateMap.clear(); + _buttonPressedMap.clear(); _poseStateMap.clear(); _collectedSamples.clear(); } return; } - PerformanceTimer perfTimer("sixense"); - if (!_hydrasConnected) { - _hydrasConnected = true; - _badDataCount = 0; - UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); + if (disconnected) { + disconnected = 0; + disconnectedInterval = 0.0f; } + PerformanceTimer perfTimer("sixense"); + // FIXME send this message once when we've positively identified hydra hardware + //UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); + #ifdef __APPLE__ SixenseBaseFunction sixenseGetMaxControllers = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); @@ -600,7 +604,6 @@ void SixenseManager::saveSettings() const { settings.setVec3Value(QString("avatarPosition"), _avatarPosition); settings.setQuatValue(QString("avatarRotation"), _avatarRotation); settings.setValue(QString("reachLength"), QVariant(_reachLength)); - settings.setValue(QString("allowedHydraFailures"), 120); } settings.endGroup(); } @@ -613,7 +616,6 @@ void SixenseManager::loadSettings() { settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); settings.getFloatValueIfValid(QString("reachLength"), _reachLength); - _allowedBadDataCount = settings.value(QString("allowedHydraFailures"), 120).toInt(); } settings.endGroup(); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index aab475963c..a44f527238 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -104,18 +104,11 @@ private: MovingAverageMap _collectedSamples; #ifdef __APPLE__ - QLibrary* _sixenseLibrary; + QLibrary* _sixenseLibrary { nullptr }; #endif - bool _hydrasConnected; - int _badDataCount; - int _allowedBadDataCount; - static const QString NAME; static const QString HYDRA_ID_STRING; - - bool _activated = false; - }; #endif // hifi_SixenseManager_h From d86f1b50a7c8a68c1d261879520eb0d915dc19ca Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 16:37:43 -0700 Subject: [PATCH 183/232] more cleanup --- interface/src/avatar/Hand.cpp | 6 ++---- interface/src/ui/ApplicationCompositor.cpp | 3 +-- libraries/avatars/src/HandData.cpp | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index dfac5e393f..15a3163998 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -51,8 +51,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { gpu::Batch& batch = *renderArgs->_batch; if (isMine) { - for (size_t i = 0; i < palms.size(); i++) { - PalmData& palm = palms[i]; + for (const auto& palm : palms) { if (!palm.isActive()) { continue; } @@ -78,8 +77,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS; // Draw the coordinate frames of the hand targets - for (size_t i = 0; i < palms.size(); ++i) { - PalmData& palm = palms[i]; + for (const auto& palm : palms) { if (palm.isActive()) { glm::vec3 root = palm.getPosition(); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 2f442a3284..2a2a45b67b 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -321,8 +321,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); auto palms = myAvatar->getHand()->getCopyOfPalms(); - for (int i = 0; i < (int)palms.size(); i++) { - const auto& palm = palms[i]; + for (const auto& palm : palms) { if (palm.isActive()) { glm::vec2 polar = getPolarCoordinates(palm); // Convert to quaternion diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 413a5ea955..fc6d18932a 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -85,8 +85,7 @@ bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float pe const PalmData*& collidingPalm) const { QReadLocker locker(&_palmsLock); - for (size_t i = 0; i < _palms.size(); ++i) { - const PalmData& palm = _palms[i]; + for (const auto& palm : _palms) { if (!palm.isActive()) { continue; } From 6ec87086bc08d43a318eb838eaa08dba5fadd685 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 27 Oct 2015 16:57:06 -0700 Subject: [PATCH 184/232] Adding the state controller --- examples/controllers/handPosesDebug.js | 42 ++++++++++++++++++- interface/resources/controllers/standard.json | 3 +- interface/src/Application.cpp | 3 +- .../src/controllers/DeviceProxy.cpp | 1 - .../controllers/src/controllers/DeviceProxy.h | 7 +++- .../src/controllers/UserInputMapper.cpp | 20 +++++---- .../src/controllers/UserInputMapper.h | 1 + 7 files changed, 62 insertions(+), 15 deletions(-) diff --git a/examples/controllers/handPosesDebug.js b/examples/controllers/handPosesDebug.js index 6c933b2565..3eabee8f53 100644 --- a/examples/controllers/handPosesDebug.js +++ b/examples/controllers/handPosesDebug.js @@ -32,6 +32,7 @@ var RIGHT_HAND = 1; var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ]; + function index(handNum, indexNum) { return handNum * NUM_HANDS + indexNum; } @@ -84,9 +85,46 @@ function updateHand(handNum, deltaTime) { } } +function updateHydra(handNum, deltaTime) { + var pose; + var handName = "right"; + if (handNum == LEFT_HAND) { + pose = Controller.getPoseValue(Controller.Hardware.Hydra.LeftHand); + handName = "left"; + } else { + pose = Controller.getPoseValue(Controller.Hardware.Hydra.RightHand); + handName = "right"; + } + + if (pose.valid) { + //print(handName + " hand moving" + JSON.stringify(pose)); + var wpos = Vec3.sum(MyAvatar.getPosition(), pose.translation); + + Overlays.editOverlay(app.spheres[index(handNum, 0)], { + position: pose.translation, + visible: true, + }); + /*var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation); + Overlays.editOverlay(app.spheres[index(handNum, 1)], { + position: vpos, + visible: true, + });*/ + } else { + Overlays.editOverlay(app.spheres[index(handNum, 0)], { + visible: false + }); + + Overlays.editOverlay(app.spheres[index(handNum, 1)], { + visible: false + }); + } +} + function update(deltaTime) { - updateHand(LEFT_HAND, deltaTime); - updateHand(RIGHT_HAND, deltaTime); + //updateHand(LEFT_HAND, deltaTime); + //updateHand(RIGHT_HAND, deltaTime); + updateHydra(LEFT_HAND, deltaTime); + updateHydra(RIGHT_HAND, deltaTime); } function scriptEnding() { diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 20177bfc5e..69193b40c5 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,8 +3,7 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", "with": "Actions.InHMD", "to": "Actions.StepYaw" }, - { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, { "from": [ "Standard.DU", "Standard.DU", "Standard.DU", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c1257e1279..403da6ddc5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2707,7 +2707,7 @@ void Application::update(float deltaTime) { userInputMapper->resetActionState(controller::Action::IN_HMD, (float)qApp->getAvatarUpdater()->isHMDMode()); userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); - userInputMapper->update(deltaTime); + // userInputMapper->update(deltaTime); bool jointsCaptured = false; for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { @@ -2718,6 +2718,7 @@ void Application::update(float deltaTime) { } } } + userInputMapper->update(deltaTime); // Transfer the user inputs to the driveKeys // FIXME can we drop drive keys and just have the avatar read the action states directly? diff --git a/libraries/controllers/src/controllers/DeviceProxy.cpp b/libraries/controllers/src/controllers/DeviceProxy.cpp index 6cbfc1048d..1bd65d4900 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.cpp +++ b/libraries/controllers/src/controllers/DeviceProxy.cpp @@ -26,6 +26,5 @@ namespace controller { return NAN; } } - } diff --git a/libraries/controllers/src/controllers/DeviceProxy.h b/libraries/controllers/src/controllers/DeviceProxy.h index 064abdbc7f..5f94e748f7 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.h +++ b/libraries/controllers/src/controllers/DeviceProxy.h @@ -23,7 +23,8 @@ namespace controller { using Modifiers = std::vector; typedef QPair InputPair; - + class Endpoint; + using EndpointPtr = std::shared_ptr; template using InputGetter = std::function; @@ -32,6 +33,7 @@ namespace controller { using PoseGetter = InputGetter; using ResetBindings = std::function; using AvailableInputGetter = std::function; + using EndpointCreator = std::function; class DeviceProxy { public: @@ -42,6 +44,9 @@ namespace controller { PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); }; AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector const { return Input::NamedVector(); }; float getValue(const Input& input, int timestamp = 0) const; + + EndpointCreator createEndpoint = [](const Input& input) -> EndpointPtr { return EndpointPtr(); }; + QString _name; }; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 67a9fdc244..8539083265 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -43,6 +43,7 @@ namespace controller { const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; + const uint16_t UserInputMapper::STATE_DEVICE = ACTIONS_DEVICE - 0xFF; const uint16_t UserInputMapper::STANDARD_DEVICE = 0; } @@ -89,13 +90,16 @@ void UserInputMapper::registerDevice(InputDevice* device) { if (_endpointsByInput.count(input)) { continue; } - Endpoint::Pointer endpoint; - if (input.device == STANDARD_DEVICE) { - endpoint = std::make_shared(input); - } else if (input.device == ACTIONS_DEVICE) { - endpoint = std::make_shared(input); - } else { - endpoint = std::make_shared(input); + + Endpoint::Pointer endpoint = proxy->createEndpoint(input); + if (!endpoint) { + if (input.device == STANDARD_DEVICE) { + endpoint = std::make_shared(input); + } else if (input.device == ACTIONS_DEVICE) { + endpoint = std::make_shared(input); + } else { + endpoint = std::make_shared(input); + } } _inputsByEndpoint[endpoint] = input; _endpointsByInput[input] = endpoint; @@ -1020,7 +1024,7 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { void UserInputMapper::resetActionState(Action action, float value) { auto endpoint = endpointFor(inputFromAction(action)); if (endpoint) { - endpoint->apply(value, 0.0f, Endpoint::Pointer()); + endpoint->apply(value, Endpoint::Pointer()); } _actionStates[toInt(action)] = value; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 40fe26aff3..311d54c11c 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -58,6 +58,7 @@ namespace controller { static const uint16_t ACTIONS_DEVICE; static const uint16_t STANDARD_DEVICE; + static const uint16_t STATE_DEVICE; UserInputMapper(); virtual ~UserInputMapper(); From 65eae3543329ac8b2585f7a24fac18cc40752c2e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 17:09:43 -0700 Subject: [PATCH 185/232] a little more cleanup --- interface/src/avatar/SkeletonModel.cpp | 31 +++++++++----------------- libraries/avatars/src/HandData.cpp | 17 -------------- libraries/avatars/src/HandData.h | 4 ---- 3 files changed, 10 insertions(+), 42 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index f821c79d59..e39bfe246b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -236,20 +236,15 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - // find the left and rightmost active palms - int leftPalmIndex, rightPalmIndex; - Hand* hand = _owningAvatar->getHand(); - - // FIXME - it's possible that the left/right hand indices could change between this call - // and the call to hand->getCopyOfPalms(); This logic should be reworked to only operate on - // the copy of the palms data - hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - // Don't Relax toward hand positions when in animGraph mode. if (!_rig->getEnableAnimGraph()) { - auto palms = hand->getCopyOfPalms(); + + Hand* hand = _owningAvatar->getHand(); + auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand); + auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand); + const float HAND_RESTORATION_RATE = 0.25f; - if (leftPalmIndex == -1 && rightPalmIndex == -1) { + if (!leftPalm.isActive() && !rightPalm.isActive()) { // palms are not yet set, use mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); @@ -259,20 +254,14 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { applyHandPosition(geometry.rightHandJointIndex, handPosition); } restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); - - } else if (leftPalmIndex == rightPalmIndex) { - // right hand only - applyPalmData(geometry.rightHandJointIndex, palms[leftPalmIndex]); - restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); - } else { - if (leftPalmIndex != -1) { - applyPalmData(geometry.leftHandJointIndex, palms[leftPalmIndex]); + if (leftPalm.isActive()) { + applyPalmData(geometry.leftHandJointIndex, leftPalm); } else { restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } - if (rightPalmIndex != -1) { - applyPalmData(geometry.rightHandJointIndex, palms[rightPalmIndex]); + if (rightPalm.isActive()) { + applyPalmData(geometry.rightHandJointIndex, rightPalm); } else { restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); } diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index fc6d18932a..5d783a671e 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -48,23 +48,6 @@ PalmData HandData::getCopyOfPalmData(Hand hand) const { return PalmData(); // invalid hand } -void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { - QReadLocker locker(&_palmsLock); - leftPalmIndex = -1; - rightPalmIndex = -1; - for (size_t i = 0; i < _palms.size(); i++) { - const PalmData& palm = _palms[i]; - if (palm.isActive()) { - if (palm.whichHand() == LeftHand) { - leftPalmIndex = i; - } - if (palm.whichHand() == RightHand) { - rightPalmIndex = i; - } - } - } -} - PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) : _rawRotation(0.0f, 0.0f, 0.0f, 1.0f), _rawPosition(0.0f), diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index b475248d9e..d782f240ee 100644 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -54,10 +54,6 @@ public: std::vector getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; } - /// Finds the indices of the left and right palms according to their locations, or -1 if either or - /// both is not found. - void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const; - /// Checks for penetration between the described sphere and the hand. /// \param penetratorCenter the center of the penetration test sphere /// \param penetratorRadius the radius of the penetration test sphere From 92ca658aae617ba217202afc58afc4c894bb8c42 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 09:23:20 -0700 Subject: [PATCH 186/232] Trying to get a state controller to work --- interface/resources/controllers/standard.json | 1 + interface/src/Application.cpp | 101 ++++++++++-------- .../src/controllers/UserInputMapper.cpp | 10 +- .../src/controllers/UserInputMapper.h | 2 - 4 files changed, 57 insertions(+), 57 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 4ef0a1b90f..7b18641636 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,6 +3,7 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, + { "from": "Standard.RX", "width": "Application.InHMD", to": "Actions.StepYaw" }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 403da6ddc5..7733fec494 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -143,6 +143,8 @@ #include "ui/UpdateDialog.h" #include "Util.h" +#include "controllers/StateController.h" + // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. #if defined(Q_OS_WIN) @@ -346,47 +348,47 @@ int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : - QApplication(argc, argv), - _dependencyManagerIsSetup(setupEssentials(argc, argv)), - _window(new MainWindow(desktop())), - _toolWindow(NULL), - _undoStackScriptingInterface(&_undoStack), - _frameCount(0), - _fps(60.0f), - _physicsEngine(new PhysicsEngine(Vectors::ZERO)), - _entities(true, this, this), - _entityClipboardRenderer(false, this, this), - _entityClipboard(new EntityTree()), - _lastQueriedTime(usecTimestampNow()), - _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), - _firstRun("firstRun", true), - _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), - _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), - _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), - _scaleMirror(1.0f), - _rotateMirror(0.0f), - _raiseMirror(0.0f), - _lastMouseMoveWasSimulated(false), - _enableProcessOctreeThread(true), - _runningScriptsWidget(NULL), - _runningScriptsWidgetWasVisible(false), - _lastNackTime(usecTimestampNow()), - _lastSendDownstreamAudioStats(usecTimestampNow()), - _aboutToQuit(false), - _notifiedPacketVersionMismatchThisDomain(false), - _maxOctreePPS(maxOctreePacketsPerSecond.get()), - _lastFaceTrackerUpdate(0) +QApplication(argc, argv), +_dependencyManagerIsSetup(setupEssentials(argc, argv)), +_window(new MainWindow(desktop())), +_toolWindow(NULL), +_undoStackScriptingInterface(&_undoStack), +_frameCount(0), +_fps(60.0f), +_physicsEngine(new PhysicsEngine(Vectors::ZERO)), +_entities(true, this, this), +_entityClipboardRenderer(false, this, this), +_entityClipboard(new EntityTree()), +_lastQueriedTime(usecTimestampNow()), +_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), +_firstRun("firstRun", true), +_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), +_scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), +_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), +_scaleMirror(1.0f), +_rotateMirror(0.0f), +_raiseMirror(0.0f), +_lastMouseMoveWasSimulated(false), +_enableProcessOctreeThread(true), +_runningScriptsWidget(NULL), +_runningScriptsWidgetWasVisible(false), +_lastNackTime(usecTimestampNow()), +_lastSendDownstreamAudioStats(usecTimestampNow()), +_aboutToQuit(false), +_notifiedPacketVersionMismatchThisDomain(false), +_maxOctreePPS(maxOctreePacketsPerSecond.get()), +_lastFaceTrackerUpdate(0) { thread()->setObjectName("Main Thread"); - + setInstance(this); - + auto controllerScriptingInterface = DependencyManager::get().data(); _controllerScriptingInterface = dynamic_cast(controllerScriptingInterface); // to work around the Qt constant wireless scanning, set the env for polling interval very high const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); - + _entityClipboard->createRootElement(); _pluginContainer = new PluginContainerProxy(); @@ -449,7 +451,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : audioIO->moveToThread(audioThread); auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); - + connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); @@ -488,7 +490,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); connect(&domainHandler, &DomainHandler::hostnameChanged, - DependencyManager::get().data(), &AddressManager::storeCurrentAddress); + DependencyManager::get().data(), &AddressManager::storeCurrentAddress); // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; @@ -499,7 +501,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // if we get a domain change, immediately attempt update location in metaverse server connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, - discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); + discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); @@ -538,14 +540,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); - #ifdef _WIN32 +#ifdef _WIN32 WSADATA WsaData; - int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); - #endif + int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData); +#endif // tell the NodeList instance who to tell the domain server we care about nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer - << NodeType::EntityServer << NodeType::AssetServer); + << NodeType::EntityServer << NodeType::AssetServer); // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); @@ -618,12 +620,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // hook up bandwidth estimator QSharedPointer bandwidthRecorder = DependencyManager::get(); connect(nodeList.data(), &LimitedNodeList::dataSent, - bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); + bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, - bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); + bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, - this, &Application::checkSkeleton, Qt::QueuedConnection); + this, &Application::checkSkeleton, Qt::QueuedConnection); // Setup the userInputMapper with the actions auto userInputMapper = DependencyManager::get(); @@ -633,9 +635,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } }); + static controller::StateController _stateController; + auto InHMDLambda = controller::StateController::ReadLambda([]() -> float { + return (float) qApp->getAvatarUpdater()->isHMDMode(); + }); + _stateController.addInputVariant("InHMD", InHMDLambda); + + userInputMapper->registerDevice(&_stateController); + // Setup the keyboardMouseDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice); + //userInputMapper->getApplicationDevice()->(float)qApp->getAvatarUpdater()->isHMDMode() + // check first run... if (_firstRun.get()) { qCDebug(interfaceapp) << "This is a first run..."; @@ -2703,9 +2715,6 @@ void Application::update(float deltaTime) { auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); - // Reflect some state into the Actions of the UserInpuMapper - userInputMapper->resetActionState(controller::Action::IN_HMD, (float)qApp->getAvatarUpdater()->isHMDMode()); - userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); // userInputMapper->update(deltaTime); diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 8539083265..257e4ebe85 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -21,6 +21,7 @@ #include #include "StandardController.h" +#include "StateController.h" #include "Logging.h" @@ -43,7 +44,6 @@ namespace controller { const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; - const uint16_t UserInputMapper::STATE_DEVICE = ACTIONS_DEVICE - 0xFF; const uint16_t UserInputMapper::STANDARD_DEVICE = 0; } @@ -1021,13 +1021,5 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { } } -void UserInputMapper::resetActionState(Action action, float value) { - auto endpoint = endpointFor(inputFromAction(action)); - if (endpoint) { - endpoint->apply(value, Endpoint::Pointer()); - } - _actionStates[toInt(action)] = value; -} - } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 311d54c11c..e28fbd740d 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -58,7 +58,6 @@ namespace controller { static const uint16_t ACTIONS_DEVICE; static const uint16_t STANDARD_DEVICE; - static const uint16_t STATE_DEVICE; UserInputMapper(); virtual ~UserInputMapper(); @@ -87,7 +86,6 @@ namespace controller { QVector getActionNames() const; Input inputFromAction(Action action) const { return getActionInputs()[toInt(action)].first; } - void resetActionState(Action action, float value); void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; } void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; } void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } From 37f967bc33c3e57a1d3973ed55a329b8f5342194 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 09:23:57 -0700 Subject: [PATCH 187/232] And adding the StateCOntroller class --- .../src/controllers/StateController.cpp | 61 +++++++++++++++++++ .../src/controllers/StateController.h | 49 +++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 libraries/controllers/src/controllers/StateController.cpp create mode 100644 libraries/controllers/src/controllers/StateController.h diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp new file mode 100644 index 0000000000..93985fe8e3 --- /dev/null +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -0,0 +1,61 @@ +// +// StateController.cpp +// controllers/src/controllers +// +// Created by Sam Gateau on 2015-10-27. +// 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 +// + +#include "StateController.h" + +#include + +#include "DeviceProxy.h" +#include "UserInputMapper.h" +#include "impl/Endpoint.h" + +namespace controller { + +StateController::StateController() : InputDevice("Application") { +} + +StateController::~StateController() { +} + +void StateController::update(float deltaTime, bool jointsCaptured) {} + +void StateController::focusOutEvent() {} + +void StateController::addInputVariant(QString name, ReadLambda& lambda) { + namedReadLambdas.push_back(NamedReadLambda(name, lambda)); +} +void StateController::buildDeviceProxy(DeviceProxy::Pointer proxy) { + proxy->_name = _name; + proxy->getButton = [this] (const Input& input, int timestamp) -> bool { return getButton(input.getChannel()); }; + proxy->getAxis = [this] (const Input& input, int timestamp) -> float { return getAxis(input.getChannel()); }; + proxy->getAvailabeInputs = [this] () -> QVector { + + + QVector availableInputs; + + int i = 0; + for (auto pair : namedReadLambdas) { + availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first)); + i++; + } + return availableInputs; + }; + proxy->createEndpoint = [this] (const Input& input) -> Endpoint::Pointer { + if (input.getChannel() < namedReadLambdas.size()) { + return std::make_shared(namedReadLambdas[input.getChannel()].second); + } + + return Endpoint::Pointer(); + }; +} + + +} diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h new file mode 100644 index 0000000000..f17b31f64b --- /dev/null +++ b/libraries/controllers/src/controllers/StateController.h @@ -0,0 +1,49 @@ +// +// StateController.h +// controllers/src/controllers +// +// Created by Sam Gateau on 2015-10-27. +// 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 +// + +#ifndef hifi_StateController_h +#define hifi_StateController_h + +#include +#include + +#include "InputDevice.h" + +namespace controller { + +class StateController : public QObject, public InputDevice { + Q_OBJECT + Q_PROPERTY(QString name READ getName) + +public: + const QString& getName() const { return _name; } + + // Device functions + virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override; + virtual QString getDefaultMappingConfig() override { return QString(); } + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + StateController(); + virtual ~StateController(); + + using ReadLambda = std::function; + using NamedReadLambda = QPair; + + void addInputVariant(QString name, ReadLambda& lambda); + + QVector namedReadLambdas; + QVector availableInputs; +}; + +} + +#endif // hifi_StateController_h \ No newline at end of file From 30f7c44f611dc644922673f1423cde1fe063995e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 28 Oct 2015 09:34:18 -0700 Subject: [PATCH 188/232] emit hardwareChanged signal to scripts --- examples/controllers/controllerMappings.js | 7 ++++++- .../controllers/src/controllers/ScriptingInterface.cpp | 1 + libraries/controllers/src/controllers/ScriptingInterface.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index 66efa63676..42494816f4 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -87,4 +87,9 @@ Object.keys(Controller.Hardware).forEach(function (deviceName) { Object.keys(Controller.Actions).forEach(function (actionName) { print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]); }); -*/ \ No newline at end of file +*/ + + +Controller.hardwareChanged.connect(function () { + print("hardwareChanged"); +}); \ No newline at end of file diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index bb09705684..b5630cfab1 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -50,6 +50,7 @@ controller::ScriptingInterface::ScriptingInterface() { // FIXME make this thread safe connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, [=] { updateMaps(); + emit hardwareChanged(); }); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index db724044fa..9af478e709 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -132,6 +132,7 @@ namespace controller { signals: void actionEvent(int action, float state); void inputEvent(int action, float state); + void hardwareChanged(); private: // Update the exposed variant maps reporting active hardware From 457ec76d3d85f69e6153b66b1d63afa0c73ab432 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 28 Oct 2015 11:21:53 -0700 Subject: [PATCH 189/232] CR feedback --- libraries/controllers/src/controllers/ScriptingInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index b5630cfab1..8d00000c45 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -48,12 +48,11 @@ controller::ScriptingInterface::ScriptingInterface() { connect(userInputMapper.data(), &UserInputMapper::inputEvent, this, &controller::ScriptingInterface::inputEvent); // FIXME make this thread safe - connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, [=] { + connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, this, [=] { updateMaps(); emit hardwareChanged(); }); - qCDebug(controllers) << "Setting up standard controller abstraction"; _standard = createDeviceMap(userInputMapper->getStandardDevice()); From fc15c7cd98f147f0b7c410904ff460bf26f6a2aa Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 12:15:14 -0700 Subject: [PATCH 190/232] Adding the ApplicationStateDevice to the APplication class and add one entry ythere --- interface/resources/controllers/standard.json | 1 - interface/src/Application.cpp | 76 ++++++++++--------- interface/src/Application.h | 5 ++ .../src/controllers/StateController.cpp | 2 +- .../src/controllers/StateController.h | 2 +- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 7b18641636..4ef0a1b90f 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,7 +3,6 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", "width": "Application.InHMD", to": "Actions.StepYaw" }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9c7c152f03..d47c2b547e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -66,6 +66,7 @@ #include #include // this should probably be removed #include +#include #include #include #include @@ -143,7 +144,6 @@ #include "ui/UpdateDialog.h" #include "Util.h" -#include "controllers/StateController.h" // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU // FIXME seems to be broken. @@ -348,36 +348,36 @@ int _keyboardFocusHighlightID{ -1 }; PluginContainer* _pluginContainer; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : -QApplication(argc, argv), -_dependencyManagerIsSetup(setupEssentials(argc, argv)), -_window(new MainWindow(desktop())), -_toolWindow(NULL), -_undoStackScriptingInterface(&_undoStack), -_frameCount(0), -_fps(60.0f), -_physicsEngine(new PhysicsEngine(Vectors::ZERO)), -_entities(true, this, this), -_entityClipboardRenderer(false, this, this), -_entityClipboard(new EntityTree()), -_lastQueriedTime(usecTimestampNow()), -_mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), -_firstRun("firstRun", true), -_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), -_scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), -_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), -_scaleMirror(1.0f), -_rotateMirror(0.0f), -_raiseMirror(0.0f), -_lastMouseMoveWasSimulated(false), -_enableProcessOctreeThread(true), -_runningScriptsWidget(NULL), -_runningScriptsWidgetWasVisible(false), -_lastNackTime(usecTimestampNow()), -_lastSendDownstreamAudioStats(usecTimestampNow()), -_aboutToQuit(false), -_notifiedPacketVersionMismatchThisDomain(false), -_maxOctreePPS(maxOctreePacketsPerSecond.get()), -_lastFaceTrackerUpdate(0) + QApplication(argc, argv), + _dependencyManagerIsSetup(setupEssentials(argc, argv)), + _window(new MainWindow(desktop())), + _toolWindow(NULL), + _undoStackScriptingInterface(&_undoStack), + _frameCount(0), + _fps(60.0f), + _physicsEngine(new PhysicsEngine(Vectors::ZERO)), + _entities(true, this, this), + _entityClipboardRenderer(false, this, this), + _entityClipboard(new EntityTree()), + _lastQueriedTime(usecTimestampNow()), + _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), + _firstRun("firstRun", true), + _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), + _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), + _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), + _scaleMirror(1.0f), + _rotateMirror(0.0f), + _raiseMirror(0.0f), + _lastMouseMoveWasSimulated(false), + _enableProcessOctreeThread(true), + _runningScriptsWidget(NULL), + _runningScriptsWidgetWasVisible(false), + _lastNackTime(usecTimestampNow()), + _lastSendDownstreamAudioStats(usecTimestampNow()), + _aboutToQuit(false), + _notifiedPacketVersionMismatchThisDomain(false), + _maxOctreePPS(maxOctreePacketsPerSecond.get()), + _lastFaceTrackerUpdate(0) { thread()->setObjectName("Main Thread"); @@ -635,13 +635,14 @@ _lastFaceTrackerUpdate(0) } }); - static controller::StateController _stateController; + // A new controllerInput device used to reflect current values from the application state + _applicationStateDevice = new controller::StateController("Application"); auto InHMDLambda = controller::StateController::ReadLambda([]() -> float { return (float) qApp->getAvatarUpdater()->isHMDMode(); }); - _stateController.addInputVariant("InHMD", InHMDLambda); + _applicationStateDevice->addInputVariant("InHMD", InHMDLambda); - userInputMapper->registerDevice(&_stateController); + userInputMapper->registerDevice(_applicationStateDevice); // Setup the keyboardMouseDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice); @@ -807,6 +808,10 @@ void Application::cleanupBeforeQuit() { AnimDebugDraw::getInstance().shutdown(); + // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete: + delete _applicationStateDevice; + _applicationStateDevice = nullptr; + if (_keyboardFocusHighlightID > 0) { getOverlays().deleteOverlay(_keyboardFocusHighlightID); _keyboardFocusHighlightID = -1; @@ -2716,7 +2721,7 @@ void Application::update(float deltaTime) { auto myAvatar = getMyAvatar(); auto userInputMapper = DependencyManager::get(); userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); - // userInputMapper->update(deltaTime); + userInputMapper->update(deltaTime); bool jointsCaptured = false; for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { @@ -2727,7 +2732,6 @@ void Application::update(float deltaTime) { } } } - userInputMapper->update(deltaTime); // Transfer the user inputs to the driveKeys // FIXME can we drop drive keys and just have the avatar read the action states directly? diff --git a/interface/src/Application.h b/interface/src/Application.h index 301eb3b262..50a39bd2eb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -71,6 +71,10 @@ class FaceTracker; class MainWindow; class AssetUpload; +namespace controller { + class StateController; +} + #ifdef Q_OS_WIN static const UINT UWM_IDENTIFY_INSTANCES = RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); @@ -442,6 +446,7 @@ private: OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers + controller::StateController* _applicationStateDevice{ nullptr }; // Default ApplicationDevice reflecting the state of different properties of the session KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad AvatarUpdate* _avatarUpdate {nullptr}; SimpleMovingAverage _avatarSimsPerSecond {10}; diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp index 93985fe8e3..ef735422db 100644 --- a/libraries/controllers/src/controllers/StateController.cpp +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -19,7 +19,7 @@ namespace controller { -StateController::StateController() : InputDevice("Application") { +StateController::StateController(QString name) : InputDevice(name) { } StateController::~StateController() { diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index f17b31f64b..afd0456b5a 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -32,7 +32,7 @@ public: virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; - StateController(); + StateController(QString name); virtual ~StateController(); using ReadLambda = std::function; From b37a6f689a2e2478409cddff5885b4bf2f9b3ca3 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 12:51:22 -0700 Subject: [PATCH 191/232] Fixing typo and review comments --- interface/resources/controllers/standard.json | 2 +- interface/src/Application.cpp | 1 - libraries/controllers/src/controllers/Actions.cpp | 2 -- libraries/controllers/src/controllers/Actions.h | 4 +--- libraries/controllers/src/controllers/StateController.cpp | 8 ++++---- libraries/controllers/src/controllers/StateController.h | 3 +-- 6 files changed, 7 insertions(+), 13 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 4ef0a1b90f..8ba9056076 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,7 +3,7 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d47c2b547e..0ccb9de28a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -647,7 +647,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Setup the keyboardMouseDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice); - //userInputMapper->getApplicationDevice()->(float)qApp->getAvatarUpdater()->isHMDMode() // check first run... if (_firstRun.get()) { diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 979c8a70c1..a9bd32b1d8 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -75,8 +75,6 @@ namespace controller { makeAxisPair(Action::BOOM_IN, "BoomIn"), makeAxisPair(Action::BOOM_OUT, "BoomOut"), - makeButtonPair(Action::IN_HMD, "InHMD"), - // Deprecated aliases // FIXME remove after we port all scripts makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 187fcd46a0..971da13cf0 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -77,9 +77,7 @@ enum class Action { // Biseced aliases for TRANSLATE_CAMERA_Z BOOM_IN, BOOM_OUT, - - IN_HMD, // THis is a read only action ? - + NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp index ef735422db..29b30bc24e 100644 --- a/libraries/controllers/src/controllers/StateController.cpp +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -30,7 +30,7 @@ void StateController::update(float deltaTime, bool jointsCaptured) {} void StateController::focusOutEvent() {} void StateController::addInputVariant(QString name, ReadLambda& lambda) { - namedReadLambdas.push_back(NamedReadLambda(name, lambda)); + _namedReadLambdas.push_back(NamedReadLambda(name, lambda)); } void StateController::buildDeviceProxy(DeviceProxy::Pointer proxy) { proxy->_name = _name; @@ -42,15 +42,15 @@ void StateController::buildDeviceProxy(DeviceProxy::Pointer proxy) { QVector availableInputs; int i = 0; - for (auto pair : namedReadLambdas) { + for (auto& pair : _namedReadLambdas) { availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first)); i++; } return availableInputs; }; proxy->createEndpoint = [this] (const Input& input) -> Endpoint::Pointer { - if (input.getChannel() < namedReadLambdas.size()) { - return std::make_shared(namedReadLambdas[input.getChannel()].second); + if (input.getChannel() < _namedReadLambdas.size()) { + return std::make_shared(_namedReadLambdas[input.getChannel()].second); } return Endpoint::Pointer(); diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index afd0456b5a..963f884d98 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -40,8 +40,7 @@ public: void addInputVariant(QString name, ReadLambda& lambda); - QVector namedReadLambdas; - QVector availableInputs; + QVector _namedReadLambdas; }; } From bbc6d9f5e7709427d1ab37b9ac4b9124dcc03a34 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 12:52:21 -0700 Subject: [PATCH 192/232] Fixing typo and review comments --- interface/src/Application.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0ccb9de28a..2c79d5a288 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2755,10 +2755,6 @@ void Application::update(float deltaTime) { myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); } - float lhc = userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK); - if (lhc != 0.0f) { - std::cout << "Left Hand click = " << lhc << std::endl; - } controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); Hand* hand = DependencyManager::get()->getMyAvatar()->getHand(); From b90af1a1ce636f6dc66d098011b8a8ac508643ab Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 13:21:45 -0700 Subject: [PATCH 193/232] Fixing typo and review comments --- examples/controllers/handPosesDebug.js | 143 +++++++----------- .../src/controllers/StateController.h | 1 + 2 files changed, 54 insertions(+), 90 deletions(-) diff --git a/examples/controllers/handPosesDebug.js b/examples/controllers/handPosesDebug.js index 3eabee8f53..e770abd957 100644 --- a/examples/controllers/handPosesDebug.js +++ b/examples/controllers/handPosesDebug.js @@ -11,17 +11,17 @@ function makeSphere(color) { - var SPHERE_SIZE = 0.05; - var sphere = Overlays.addOverlay("sphere", { - position: { x: 0, y: 0, z: 0 }, - size: SPHERE_SIZE, - color: color, - alpha: 1.0, - solid: true, - visible: true, - }); + var SPHERE_SIZE = 0.05; + var sphere = Overlays.addOverlay("sphere", { + position: { x: 0, y: 0, z: 0 }, + size: SPHERE_SIZE, + color: color, + alpha: 1.0, + solid: true, + visible: true, + }); - return sphere; + return sphere; } @@ -34,104 +34,67 @@ var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } function index(handNum, indexNum) { - return handNum * NUM_HANDS + indexNum; + return handNum * NUM_HANDS + indexNum; } var app = {}; function setup() { - app.spheres = new Array(); + app.spheres = new Array(); - for (var h = 0; h < NUM_HANDS; h++) { - for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) { - var i = index(h, s); - app.spheres[i] = makeSphere(COLORS[h]); - print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i])); - } - } + for (var h = 0; h < NUM_HANDS; h++) { + for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) { + var i = index(h, s); + app.spheres[i] = makeSphere(COLORS[h]); + print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i])); + } + } } function updateHand(handNum, deltaTime) { - var pose; - var handName = "right"; - if (handNum == LEFT_HAND) { - pose = MyAvatar.getLeftHandPose(); - handName = "left"; - } else { - pose = MyAvatar.getRightHandPose(); - handName = "right"; - } + var pose; + var handName = "right"; + if (handNum == LEFT_HAND) { + pose = MyAvatar.getLeftHandPose(); + handName = "left"; + } else { + pose = MyAvatar.getRightHandPose(); + handName = "right"; + } - if (pose.valid) { - //print(handName + " hand moving" + JSON.stringify(pose)); - Overlays.editOverlay(app.spheres[index(handNum, 0)], { - position: pose.translation, - visible: true, - }); - var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation); - Overlays.editOverlay(app.spheres[index(handNum, 1)], { - position: vpos, - visible: true, - }); - } else { - Overlays.editOverlay(app.spheres[index(handNum, 0)], { - visible: false - }); + if (pose.valid) { + //print(handName + " hand moving" + JSON.stringify(pose)); + Overlays.editOverlay(app.spheres[index(handNum, 0)], { + position: pose.translation, + visible: true, + }); + var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation); + Overlays.editOverlay(app.spheres[index(handNum, 1)], { + position: vpos, + visible: true, + }); + } else { + Overlays.editOverlay(app.spheres[index(handNum, 0)], { + visible: false + }); - Overlays.editOverlay(app.spheres[index(handNum, 1)], { - visible: false - }); - } -} - -function updateHydra(handNum, deltaTime) { - var pose; - var handName = "right"; - if (handNum == LEFT_HAND) { - pose = Controller.getPoseValue(Controller.Hardware.Hydra.LeftHand); - handName = "left"; - } else { - pose = Controller.getPoseValue(Controller.Hardware.Hydra.RightHand); - handName = "right"; - } - - if (pose.valid) { - //print(handName + " hand moving" + JSON.stringify(pose)); - var wpos = Vec3.sum(MyAvatar.getPosition(), pose.translation); - - Overlays.editOverlay(app.spheres[index(handNum, 0)], { - position: pose.translation, - visible: true, - }); - /*var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation); - Overlays.editOverlay(app.spheres[index(handNum, 1)], { - position: vpos, - visible: true, - });*/ - } else { - Overlays.editOverlay(app.spheres[index(handNum, 0)], { - visible: false - }); - - Overlays.editOverlay(app.spheres[index(handNum, 1)], { - visible: false - }); - } + Overlays.editOverlay(app.spheres[index(handNum, 1)], { + visible: false + }); + } } function update(deltaTime) { - //updateHand(LEFT_HAND, deltaTime); - //updateHand(RIGHT_HAND, deltaTime); - updateHydra(LEFT_HAND, deltaTime); - updateHydra(RIGHT_HAND, deltaTime); + updateHand(LEFT_HAND, deltaTime); + updateHand(RIGHT_HAND, deltaTime); } function scriptEnding() { - print("Removing spheres = " + JSON.stringify(app.spheres)); - for (var i = 0; i < app.spheres.length; i++) { - Overlays.deleteOverlay(app.spheres[i]); - } + print("Removing spheres = " + JSON.stringify(app.spheres)); + for (var i = 0; i < app.spheres.length; i++) { + Overlays.deleteOverlay(app.spheres[i]); + } } setup(); diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index 963f884d98..fad3b0abba 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -40,6 +40,7 @@ public: void addInputVariant(QString name, ReadLambda& lambda); +protected: QVector _namedReadLambdas; }; From d61b3e5790d5ee78d366836ec060fdec310a800d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 28 Oct 2015 17:41:52 -0700 Subject: [PATCH 194/232] Fixing controller link requirements --- tests/controllers/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/controllers/CMakeLists.txt b/tests/controllers/CMakeLists.txt index d9bef079ff..d1c8464dd5 100644 --- a/tests/controllers/CMakeLists.txt +++ b/tests/controllers/CMakeLists.txt @@ -6,7 +6,7 @@ setup_hifi_project(Script Qml) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared script-engine plugins input-plugins display-plugins controllers) +link_hifi_libraries(shared gl script-engine plugins render-utils input-plugins display-plugins controllers) if (WIN32) From f01b4dcd9060c3c49f74db69fb6f836eb578b86b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 29 Oct 2015 09:23:19 -0700 Subject: [PATCH 195/232] update cmake to work with newer smi runtime --- cmake/modules/FindiViewHMD.cmake | 38 +++++--------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/cmake/modules/FindiViewHMD.cmake b/cmake/modules/FindiViewHMD.cmake index f7b13f8124..e408c92380 100644 --- a/cmake/modules/FindiViewHMD.cmake +++ b/cmake/modules/FindiViewHMD.cmake @@ -21,45 +21,17 @@ if (WIN32) hifi_library_search_hints("iViewHMD") find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS}) list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH) - set(IVIEWHMD_DLLS - avcodec-53.dll - avformat-53.dll - avutil-51.dll - libboost_filesystem-mgw45-mt-1_49.dll - libboost_system-mgw45-mt-1_49.dll - libboost_thread-mgw45-mt-1_49.dll - libgcc_s_dw2-1.dll - libiViewNG-LibCore.dll - libopencv_calib3d244.dll - libopencv_core244.dll - libopencv_features2d244.dll - libopencv_flann244.dll - libopencv_highgui244.dll - libopencv_imgproc244.dll - libopencv_legacy244.dll - libopencv_ml244.dll - libopencv_video244.dll - libstdc++-6.dll - opencv_core220.dll - opencv_highgui220.dll - opencv_imgproc220.dll - swscale-2.dll - ) - - foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS}) - find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) - list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH) - list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH}) - endforeach() + find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) + list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS}) - add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS}) + add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATH_3RD_PARTY}) mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) From c4e82a85d9f52124353d92d16900f9a39f055725 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 28 Oct 2015 15:40:52 -0700 Subject: [PATCH 196/232] Remove DeviceProxy in favor of InputDevice --- interface/src/Application.cpp | 17 +- interface/src/Application.h | 4 +- interface/src/Menu.cpp | 2 + interface/src/devices/3DConnexionClient.cpp | 3 + interface/src/devices/3DConnexionClient.h | 3 + .../controllers/src/controllers/Actions.cpp | 152 +++++++++--------- .../controllers/src/controllers/Actions.h | 13 +- .../src/controllers/DeviceProxy.cpp | 16 -- .../controllers/src/controllers/DeviceProxy.h | 11 +- .../src/controllers/InputDevice.cpp | 39 ++++- .../controllers/src/controllers/InputDevice.h | 34 ++-- .../src/controllers/ScriptingInterface.cpp | 6 +- .../src/controllers/StandardController.cpp | 114 +++++++------ .../src/controllers/StandardController.h | 8 +- .../src/controllers/StandardControls.h | 2 + .../src/controllers/StateController.cpp | 35 ++-- .../src/controllers/StateController.h | 5 +- .../src/controllers/UserInputMapper.cpp | 47 +++--- .../src/controllers/UserInputMapper.h | 15 +- .../impl/endpoints/InputEndpoint.cpp | 8 +- libraries/input-plugins/CMakeLists.txt | 2 +- .../src/input-plugins/Joystick.cpp | 97 ++++++----- .../src/input-plugins/Joystick.h | 5 +- .../src/input-plugins/KeyboardMouseDevice.cpp | 38 +++-- .../src/input-plugins/KeyboardMouseDevice.h | 14 +- .../src/input-plugins/SDL2Manager.cpp | 24 +-- .../src/input-plugins/SDL2Manager.h | 2 +- .../src/input-plugins/SixenseManager.cpp | 66 ++++---- .../src/input-plugins/SixenseManager.h | 4 +- .../input-plugins/ViveControllerManager.cpp | 87 ++++------ .../src/input-plugins/ViveControllerManager.h | 4 +- tests/controllers/src/main.cpp | 2 +- 32 files changed, 421 insertions(+), 458 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9c4465aa78..4e4b4010f0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -636,7 +636,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : }); // A new controllerInput device used to reflect current values from the application state - _applicationStateDevice = new controller::StateController("Application"); + _applicationStateDevice = std::make_shared(); auto InHMDLambda = controller::StateController::ReadLambda([]() -> float { return (float) qApp->getAvatarUpdater()->isHMDMode(); }); @@ -719,8 +719,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Now that menu is initalized we can sync myAvatar with it's state. getMyAvatar()->updateMotionBehaviorFromMenu(); +#if 0 // the 3Dconnexion device wants to be initiliazed after a window is displayed. ConnexionClient::getInstance().init(); +#endif auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); @@ -808,8 +810,7 @@ void Application::cleanupBeforeQuit() { AnimDebugDraw::getInstance().shutdown(); // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete: - delete _applicationStateDevice; - _applicationStateDevice = nullptr; + _applicationStateDevice.reset(); if (_keyboardFocusHighlightID > 0) { getOverlays().deleteOverlay(_keyboardFocusHighlightID); @@ -921,7 +922,10 @@ Application::~Application() { Leapmotion::destroy(); RealSense::destroy(); + +#if 0 ConnexionClient::getInstance().destroy(); +#endif qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages } @@ -1024,7 +1028,10 @@ void Application::initializeUi() { foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { QString name = inputPlugin->getName(); if (name == KeyboardMouseDevice::NAME) { - _keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky + auto kbm = static_cast(inputPlugin.data()); + // FIXME incredibly evil.... _keyboardMouseDevice is now owned by + // both a QSharedPointer and a std::shared_ptr + _keyboardMouseDevice = std::shared_ptr(kbm); } } updateInputModes(); @@ -1824,7 +1831,9 @@ void Application::focusOutEvent(QFocusEvent* event) { inputPlugin->pluginFocusOutEvent(); } } +#if 0 ConnexionData::getInstance().focusOutEvent(); +#endif // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 50a39bd2eb..bf45a0c6ec 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -446,8 +446,8 @@ private: OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers - controller::StateController* _applicationStateDevice{ nullptr }; // Default ApplicationDevice reflecting the state of different properties of the session - KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad + std::shared_ptr _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session + std::shared_ptr _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad AvatarUpdate* _avatarUpdate {nullptr}; SimpleMovingAverage _avatarSimsPerSecond {10}; int _avatarSimsPerSecondReport {0}; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a393ca5316..1565db2905 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -464,11 +464,13 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true, avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); +#if 0 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, &ConnexionClient::getInstance(), SLOT(toggleConnexion(bool))); +#endif addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); diff --git a/interface/src/devices/3DConnexionClient.cpp b/interface/src/devices/3DConnexionClient.cpp index 7c44e4eed7..38a9b4cb29 100755 --- a/interface/src/devices/3DConnexionClient.cpp +++ b/interface/src/devices/3DConnexionClient.cpp @@ -9,8 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + #include "3DConnexionClient.h" +#if 0 #include #include @@ -967,3 +969,4 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes #endif // __APPLE__ #endif // HAVE_3DCONNEXIONCLIENT +#endif \ No newline at end of file diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index 8489d54913..03a43d4c64 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -11,6 +11,7 @@ #ifndef hifi_3DConnexionClient_h #define hifi_3DConnexionClient_h +#if 0 #include #include #include @@ -217,4 +218,6 @@ public: void handleAxisEvent(); }; +#endif + #endif // defined(hifi_3DConnexionClient_h) diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index a9bd32b1d8..a4a8656fb7 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -8,7 +8,7 @@ #include "Actions.h" -#include "UserInputMapper.h" +#include "impl/endpoints/ActionEndpoint.h" namespace controller { @@ -29,86 +29,80 @@ namespace controller { return makePair(ChannelType::POSE, action, name); } - // Device functions - void ActionsDevice::buildDeviceProxy(DeviceProxy::Pointer proxy) { - proxy->_name = _name; - proxy->getButton = [this](const Input& input, int timestamp) -> bool { return false; }; - proxy->getAxis = [this](const Input& input, int timestamp) -> float { return 0; }; - proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs{ - makeAxisPair(Action::TRANSLATE_X, "TranslateX"), - makeAxisPair(Action::TRANSLATE_Y, "TranslateY"), - makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"), - makeAxisPair(Action::ROLL, "Roll"), - makeAxisPair(Action::PITCH, "Pitch"), - makeAxisPair(Action::YAW, "Yaw"), - makeAxisPair(Action::STEP_YAW, "StepYaw"), - makeAxisPair(Action::STEP_PITCH, "StepPitch"), - makeAxisPair(Action::STEP_ROLL, "StepRoll"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), - makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), - - makePosePair(Action::LEFT_HAND, "LeftHand"), - makePosePair(Action::RIGHT_HAND, "RightHand"), - - makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), - makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), - - makeButtonPair(Action::SHIFT, "Shift"), - makeButtonPair(Action::ACTION1, "PrimaryAction"), - makeButtonPair(Action::ACTION2, "SecondaryAction"), - makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), - makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), - - // Aliases and bisected versions - makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), - makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), - makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), - makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"), - makeAxisPair(Action::VERTICAL_DOWN, "Down"), - makeAxisPair(Action::VERTICAL_UP, "Up"), - makeAxisPair(Action::YAW_LEFT, "YawLeft"), - makeAxisPair(Action::YAW_RIGHT, "YawRight"), - makeAxisPair(Action::PITCH_DOWN, "PitchDown"), - makeAxisPair(Action::PITCH_UP, "PitchUp"), - makeAxisPair(Action::BOOM_IN, "BoomIn"), - makeAxisPair(Action::BOOM_OUT, "BoomOut"), - - // Deprecated aliases - // FIXME remove after we port all scripts - makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), - makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"), - makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"), - makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"), - makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"), - makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"), - makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"), - makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"), - makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"), - makeAxisPair(Action::PITCH_UP, "PITCH_UP"), - makeAxisPair(Action::BOOM_IN, "BOOM_IN"), - makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"), - - makePosePair(Action::LEFT_HAND, "LEFT_HAND"), - makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"), - - makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"), - makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"), - - makeButtonPair(Action::SHIFT, "SHIFT"), - makeButtonPair(Action::ACTION1, "ACTION1"), - makeButtonPair(Action::ACTION2, "ACTION2"), - makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"), - makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"), - }; - return availableInputs; - }; - + EndpointPointer ActionsDevice::createEndpoint(const Input& input) const { + return std::make_shared(input); } - QString ActionsDevice::getDefaultMappingConfig() { - return QString(); + // Device functions + Input::NamedVector ActionsDevice::getAvailableInputs() const { + static Input::NamedVector availableInputs { + makeAxisPair(Action::TRANSLATE_X, "TranslateX"), + makeAxisPair(Action::TRANSLATE_Y, "TranslateY"), + makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"), + makeAxisPair(Action::ROLL, "Roll"), + makeAxisPair(Action::PITCH, "Pitch"), + makeAxisPair(Action::YAW, "Yaw"), + makeAxisPair(Action::STEP_YAW, "StepYaw"), + makeAxisPair(Action::STEP_PITCH, "StepPitch"), + makeAxisPair(Action::STEP_ROLL, "StepRoll"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), + makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), + + makePosePair(Action::LEFT_HAND, "LeftHand"), + makePosePair(Action::RIGHT_HAND, "RightHand"), + + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), + makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), + + makeButtonPair(Action::SHIFT, "Shift"), + makeButtonPair(Action::ACTION1, "PrimaryAction"), + makeButtonPair(Action::ACTION2, "SecondaryAction"), + makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), + makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), + + // Aliases and bisected versions + makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), + makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), + makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), + makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"), + makeAxisPair(Action::VERTICAL_DOWN, "Down"), + makeAxisPair(Action::VERTICAL_UP, "Up"), + makeAxisPair(Action::YAW_LEFT, "YawLeft"), + makeAxisPair(Action::YAW_RIGHT, "YawRight"), + makeAxisPair(Action::PITCH_DOWN, "PitchDown"), + makeAxisPair(Action::PITCH_UP, "PitchUp"), + makeAxisPair(Action::BOOM_IN, "BoomIn"), + makeAxisPair(Action::BOOM_OUT, "BoomOut"), + + // Deprecated aliases + // FIXME remove after we port all scripts + makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), + makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"), + makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"), + makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"), + makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"), + makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"), + makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"), + makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"), + makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"), + makeAxisPair(Action::PITCH_UP, "PITCH_UP"), + makeAxisPair(Action::BOOM_IN, "BOOM_IN"), + makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"), + + makePosePair(Action::LEFT_HAND, "LEFT_HAND"), + makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"), + + makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"), + makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"), + + makeButtonPair(Action::SHIFT, "SHIFT"), + makeButtonPair(Action::ACTION1, "ACTION1"), + makeButtonPair(Action::ACTION2, "ACTION2"), + makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"), + makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"), + }; + return availableInputs; } void ActionsDevice::update(float deltaTime, bool jointsCaptured) { diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 971da13cf0..36df695032 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -89,11 +89,8 @@ class ActionsDevice : public QObject, public InputDevice { Q_PROPERTY(QString name READ getName) public: - const QString& getName() const { return _name; } - - // Device functions - virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override; + virtual EndpointPointer createEndpoint(const Input& input) const override; + virtual Input::NamedVector getAvailableInputs() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; @@ -103,10 +100,4 @@ public: } - - - -#include "StandardControls.h" - - #endif // hifi_StandardController_h diff --git a/libraries/controllers/src/controllers/DeviceProxy.cpp b/libraries/controllers/src/controllers/DeviceProxy.cpp index 1bd65d4900..f3e9526080 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.cpp +++ b/libraries/controllers/src/controllers/DeviceProxy.cpp @@ -10,21 +10,5 @@ #include "DeviceProxy.h" namespace controller { - - float DeviceProxy::getValue(const Input& input, int timestamp) const { - switch (input.getType()) { - case ChannelType::BUTTON: - return getButton(input, timestamp) ? 1.0f : 0.0f; - - case ChannelType::AXIS: - return getAxis(input, timestamp); - - case ChannelType::POSE: - return getPose(input, timestamp).valid ? 1.0f : 0.0f; - - default: - return NAN; - } - } } diff --git a/libraries/controllers/src/controllers/DeviceProxy.h b/libraries/controllers/src/controllers/DeviceProxy.h index 5f94e748f7..83a813d5d5 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.h +++ b/libraries/controllers/src/controllers/DeviceProxy.h @@ -20,7 +20,7 @@ #include "Pose.h" namespace controller { - + /* using Modifiers = std::vector; typedef QPair InputPair; class Endpoint; @@ -38,17 +38,10 @@ namespace controller { class DeviceProxy { public: using Pointer = std::shared_ptr; - const QString& getName() const { return _name; } - ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; - AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; - PoseGetter getPose = [](const Input& input, int timestamp) -> Pose { return Pose(); }; - AvailableInputGetter getAvailabeInputs = []() -> Input::NamedVector const { return Input::NamedVector(); }; - float getValue(const Input& input, int timestamp = 0) const; - - EndpointCreator createEndpoint = [](const Input& input) -> EndpointPtr { return EndpointPtr(); }; QString _name; }; + */ } #endif diff --git a/libraries/controllers/src/controllers/InputDevice.cpp b/libraries/controllers/src/controllers/InputDevice.cpp index 78e920d4b5..d5044a219f 100644 --- a/libraries/controllers/src/controllers/InputDevice.cpp +++ b/libraries/controllers/src/controllers/InputDevice.cpp @@ -11,6 +11,7 @@ #include "InputDevice.h" #include "Input.h" +#include "impl/endpoints/InputEndpoint.h" namespace controller { @@ -59,29 +60,55 @@ namespace controller { } } - Input InputDevice::makeInput(controller::StandardButtonChannel button) { + Input InputDevice::makeInput(controller::StandardButtonChannel button) const { return Input(_deviceID, button, ChannelType::BUTTON); } - Input InputDevice::makeInput(controller::StandardAxisChannel axis) { + Input InputDevice::makeInput(controller::StandardAxisChannel axis) const { return Input(_deviceID, axis, ChannelType::AXIS); } - Input InputDevice::makeInput(controller::StandardPoseChannel pose) { + Input InputDevice::makeInput(controller::StandardPoseChannel pose) const { return Input(_deviceID, pose, ChannelType::POSE); } - Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) { + Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) const { return Input::NamedPair(makeInput(button), name); } - Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) { + Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) const { return Input::NamedPair(makeInput(axis), name); } - Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) { + Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) const { return Input::NamedPair(makeInput(pose), name); } + float InputDevice::getValue(ChannelType channelType, uint16_t channel) const { + switch (channelType) { + case ChannelType::AXIS: + return getAxis(channel); + + case ChannelType::BUTTON: + return getButton(channel); + + case ChannelType::POSE: + return getPose(channel).valid ? 1.0f : 0.0f; + + default: + break; + } + + return 0.0f; + } + + + float InputDevice::getValue(const Input& input) const { + return getValue(input.getType(), input.channel); + } + + EndpointPointer InputDevice::createEndpoint(const Input& input) const { + return std::make_shared(input); + } } diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index e01def2368..fc3477b41a 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -21,12 +21,16 @@ #include "StandardControls.h" #include "DeviceProxy.h" + // Event types for each controller const unsigned int CONTROLLER_0_EVENT = 1500U; const unsigned int CONTROLLER_1_EVENT = 1501U; namespace controller { +class Endpoint; +using EndpointPointer = std::shared_ptr; + // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first. // e.g. class Example : public InputPlugin, public InputDevice // instead of class Example : public InputDevice, public InputPlugin @@ -45,8 +49,11 @@ public: float getAxis(int channel) const; Pose getPose(int channel) const; - virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) = 0; - virtual QString getDefaultMappingConfig() = 0; + float getValue(const Input& input) const; + float getValue(ChannelType channelType, uint16_t channel) const; + Pose getPoseValue(uint16_t channel) const; + + const QString& getName() const { return _name; } // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas @@ -63,20 +70,25 @@ public: static bool getLowVelocityFilter() { return _lowVelocityFilter; }; - Input makeInput(StandardButtonChannel button); - Input makeInput(StandardAxisChannel axis); - Input makeInput(StandardPoseChannel pose); - Input::NamedPair makePair(StandardButtonChannel button, const QString& name); - Input::NamedPair makePair(StandardAxisChannel button, const QString& name); - Input::NamedPair makePair(StandardPoseChannel button, const QString& name); - public slots: + Input makeInput(StandardButtonChannel button) const; + Input makeInput(StandardAxisChannel axis) const; + Input makeInput(StandardPoseChannel pose) const; + Input::NamedPair makePair(StandardButtonChannel button, const QString& name) const; + Input::NamedPair makePair(StandardAxisChannel button, const QString& name) const; + Input::NamedPair makePair(StandardPoseChannel button, const QString& name) const; +public slots: static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; protected: friend class UserInputMapper; - uint16_t _deviceID{ Input::INVALID_DEVICE }; - QString _name; + virtual Input::NamedVector getAvailableInputs() const = 0; + virtual QString getDefaultMappingConfig() const { return QString(); } + virtual EndpointPointer createEndpoint(const Input& input) const; + + uint16_t _deviceID { Input::INVALID_DEVICE }; + + const QString _name; ButtonPressedMap _buttonPressedMap; AxisStateMap _axisStateMap; diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 8d00000c45..e49248e8f0 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -27,10 +27,10 @@ static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; -static QVariantMap createDeviceMap(const controller::DeviceProxy::Pointer device) { +static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device) { auto userInputMapper = DependencyManager::get(); QVariantMap deviceMap; - for (const auto& inputMapping : device->getAvailabeInputs()) { + for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) { const auto& input = inputMapping.first; const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION); qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() @@ -179,7 +179,7 @@ namespace controller { return DependencyManager::get()->getDeviceName((unsigned short)device); } - QVector ScriptingInterface::getAvailableInputs(unsigned int device) { + QVector ScriptingInterface::getAvailableInputs(unsigned int device) { return DependencyManager::get()->getAvailableInputs((unsigned short)device); } diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index fc62f85b81..44f1bff1ae 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -13,8 +13,8 @@ #include -#include "DeviceProxy.h" #include "UserInputMapper.h" +#include "impl/endpoints/StandardEndpoint.h" namespace controller { @@ -33,91 +33,87 @@ void StandardController::focusOutEvent() { _buttonPressedMap.clear(); }; -void StandardController::buildDeviceProxy(DeviceProxy::Pointer proxy) { - proxy->_name = _name; - proxy->getButton = [this] (const Input& input, int timestamp) -> bool { return getButton(input.getChannel()); }; - proxy->getAxis = [this] (const Input& input, int timestamp) -> float { return getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; +Input::NamedVector StandardController::getAvailableInputs() const { + static Input::NamedVector availableInputs { // Buttons - availableInputs.append(makePair(A, "A")); - availableInputs.append(makePair(B, "B")); - availableInputs.append(makePair(X, "X")); - availableInputs.append(makePair(Y, "Y")); + makePair(A, "A"), + makePair(B, "B"), + makePair(X, "X"), + makePair(Y, "Y"), // DPad - availableInputs.append(makePair(DU, "DU")); - availableInputs.append(makePair(DD, "DD")); - availableInputs.append(makePair(DL, "DL")); - availableInputs.append(makePair(DR, "DR")); + makePair(DU, "DU"), + makePair(DD, "DD"), + makePair(DL, "DL"), + makePair(DR, "DR"), // Bumpers - availableInputs.append(makePair(LB, "LB")); - availableInputs.append(makePair(RB, "RB")); + makePair(LB, "LB"), + makePair(RB, "RB"), // Stick press - availableInputs.append(makePair(LS, "LS")); - availableInputs.append(makePair(RS, "RS")); + makePair(LS, "LS"), + makePair(RS, "RS"), // Center buttons - availableInputs.append(makePair(START, "Start")); - availableInputs.append(makePair(BACK, "Back")); + makePair(START, "Start"), + makePair(BACK, "Back"), // Analog sticks - availableInputs.append(makePair(LY, "LY")); - availableInputs.append(makePair(LX, "LX")); - availableInputs.append(makePair(RY, "RY")); - availableInputs.append(makePair(RX, "RX")); + makePair(LY, "LY"), + makePair(LX, "LX"), + makePair(RY, "RY"), + makePair(RX, "RX"), // Triggers - availableInputs.append(makePair(LT, "LT")); - availableInputs.append(makePair(RT, "RT")); + makePair(LT, "LT"), + makePair(RT, "RT"), // Finger abstractions - availableInputs.append(makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb")); - availableInputs.append(makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb")); - availableInputs.append(makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb")); - availableInputs.append(makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb")); + makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), + makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), + makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), + makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), - availableInputs.append(makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex")); - availableInputs.append(makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex")); - availableInputs.append(makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex")); - availableInputs.append(makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex")); + makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"), + makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"), + makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"), + makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"), - availableInputs.append(makePair(LEFT_GRIP, "LeftGrip")); - availableInputs.append(makePair(RIGHT_GRIP, "RightGrip")); + makePair(LEFT_GRIP, "LeftGrip"), + makePair(RIGHT_GRIP, "RightGrip"), // Poses - availableInputs.append(makePair(LEFT_HAND, "LeftHand")); - availableInputs.append(makePair(RIGHT_HAND, "RightHand")); + makePair(LEFT_HAND, "LeftHand"), + makePair(RIGHT_HAND, "RightHand"), // Aliases, PlayStation style names - availableInputs.append(makePair(LB, "L1")); - availableInputs.append(makePair(RB, "R1")); - availableInputs.append(makePair(LT, "L2")); - availableInputs.append(makePair(RT, "R2")); - availableInputs.append(makePair(LS, "L3")); - availableInputs.append(makePair(RS, "R3")); - availableInputs.append(makePair(BACK, "Select")); - availableInputs.append(makePair(A, "Cross")); - availableInputs.append(makePair(B, "Circle")); - availableInputs.append(makePair(X, "Square")); - availableInputs.append(makePair(Y, "Triangle")); - availableInputs.append(makePair(DU, "Up")); - availableInputs.append(makePair(DD, "Down")); - availableInputs.append(makePair(DL, "Left")); - availableInputs.append(makePair(DR, "Right")); - - - - return availableInputs; + makePair(LB, "L1"), + makePair(RB, "R1"), + makePair(LT, "L2"), + makePair(RT, "R2"), + makePair(LS, "L3"), + makePair(RS, "R3"), + makePair(BACK, "Select"), + makePair(A, "Cross"), + makePair(B, "Circle"), + makePair(X, "Square"), + makePair(Y, "Triangle"), + makePair(DU, "Up"), + makePair(DD, "Down"), + makePair(DL, "Left"), + makePair(DR, "Right"), }; + return availableInputs; } +EndpointPointer StandardController::createEndpoint(const Input& input) const { + return std::make_shared(input); +} -QString StandardController::getDefaultMappingConfig() { +QString StandardController::getDefaultMappingConfig() const { static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json"; return DEFAULT_MAPPING_JSON; } diff --git a/libraries/controllers/src/controllers/StandardController.h b/libraries/controllers/src/controllers/StandardController.h index 4aad513553..6c18c76371 100644 --- a/libraries/controllers/src/controllers/StandardController.h +++ b/libraries/controllers/src/controllers/StandardController.h @@ -25,11 +25,9 @@ class StandardController : public QObject, public InputDevice { Q_PROPERTY(QString name READ getName) public: - const QString& getName() const { return _name; } - - // Device functions - virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override; + virtual EndpointPointer createEndpoint(const Input& input) const override; + virtual Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index 066a3e5e60..9c6defb865 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -58,9 +58,11 @@ namespace controller { // Left Analog stick LX = 0, LY, + LZ, // Right Analog stick RX, RY, + RZ, // Triggers LT, RT, diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp index 29b30bc24e..efe7a064fc 100644 --- a/libraries/controllers/src/controllers/StateController.cpp +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -19,7 +19,7 @@ namespace controller { -StateController::StateController(QString name) : InputDevice(name) { +StateController::StateController() : InputDevice("Application") { } StateController::~StateController() { @@ -32,30 +32,15 @@ void StateController::focusOutEvent() {} void StateController::addInputVariant(QString name, ReadLambda& lambda) { _namedReadLambdas.push_back(NamedReadLambda(name, lambda)); } -void StateController::buildDeviceProxy(DeviceProxy::Pointer proxy) { - proxy->_name = _name; - proxy->getButton = [this] (const Input& input, int timestamp) -> bool { return getButton(input.getChannel()); }; - proxy->getAxis = [this] (const Input& input, int timestamp) -> float { return getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - - - QVector availableInputs; - - int i = 0; - for (auto& pair : _namedReadLambdas) { - availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first)); - i++; - } - return availableInputs; - }; - proxy->createEndpoint = [this] (const Input& input) -> Endpoint::Pointer { - if (input.getChannel() < _namedReadLambdas.size()) { - return std::make_shared(_namedReadLambdas[input.getChannel()].second); - } - return Endpoint::Pointer(); - }; +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)); + i++; + } + return availableInputs; } - -} +} \ No newline at end of file diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index fad3b0abba..d664c6b8d0 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -27,12 +27,11 @@ public: const QString& getName() const { return _name; } // Device functions - virtual void buildDeviceProxy(DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override { return QString(); } + virtual Input::NamedVector getAvailableInputs() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; - StateController(QString name); + StateController(); virtual ~StateController(); using ReadLambda = std::function; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 257e4ebe85..64a2a54ef4 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -49,13 +49,13 @@ namespace controller { // Default contruct allocate the poutput size with the current hardcoded action channels controller::UserInputMapper::UserInputMapper() { - _standardController = std::make_shared(); - registerDevice(new ActionsDevice()); - registerDevice(_standardController.get()); + registerDevice(std::make_shared()); + registerDevice(std::make_shared()); } namespace controller { + UserInputMapper::~UserInputMapper() { } @@ -67,31 +67,24 @@ int UserInputMapper::recordDeviceOfType(const QString& deviceName) { return _deviceCounts[deviceName]; } -void UserInputMapper::registerDevice(InputDevice* device) { +void UserInputMapper::registerDevice(InputDevice::Pointer device) { Locker locker(_lock); if (device->_deviceID == Input::INVALID_DEVICE) { device->_deviceID = getFreeDeviceID(); } const auto& deviceID = device->_deviceID; - DeviceProxy::Pointer proxy = std::make_shared(); - proxy->_name = device->_name; - device->buildDeviceProxy(proxy); - int numberOfType = recordDeviceOfType(proxy->_name); - if (numberOfType > 1) { - proxy->_name += QString::number(numberOfType); - } + int numberOfType = recordDeviceOfType(device->getName()); - qCDebug(controllers) << "Registered input device <" << proxy->_name << "> deviceID = " << deviceID; - - for (const auto& inputMapping : proxy->getAvailabeInputs()) { + qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID; + for (const auto& inputMapping : device->getAvailableInputs()) { const auto& input = inputMapping.first; // Ignore aliases if (_endpointsByInput.count(input)) { continue; } - Endpoint::Pointer endpoint = proxy->createEndpoint(input); + Endpoint::Pointer endpoint = device->createEndpoint(input); if (!endpoint) { if (input.device == STANDARD_DEVICE) { endpoint = std::make_shared(input); @@ -105,7 +98,7 @@ void UserInputMapper::registerDevice(InputDevice* device) { _endpointsByInput[input] = endpoint; } - _registeredDevices[deviceID] = proxy; + _registeredDevices[deviceID] = device; auto mapping = loadMapping(device->getDefaultMappingConfig()); if (mapping) { _mappingsByDevice[deviceID] = mapping; @@ -136,13 +129,13 @@ void UserInputMapper::removeDevice(int deviceID) { } -DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { +InputDevice::Pointer UserInputMapper::getDevice(const Input& input) { Locker locker(_lock); auto device = _registeredDevices.find(input.getDevice()); if (device != _registeredDevices.end()) { return (device->second); } else { - return DeviceProxy::Pointer(); + return InputDevice::Pointer(); } } @@ -190,8 +183,8 @@ Input UserInputMapper::findDeviceInput(const QString& inputName) const { int deviceID = findDevice(deviceName); if (deviceID != Input::INVALID_DEVICE) { - const auto& deviceProxy = _registeredDevices.at(deviceID); - auto deviceInputs = deviceProxy->getAvailabeInputs(); + const auto& device = _registeredDevices.at(deviceID); + auto deviceInputs = device->getAvailableInputs(); for (auto input : deviceInputs) { if (input.second == inputName) { @@ -273,7 +266,7 @@ void UserInputMapper::update(float deltaTime) { Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { Locker locker(_lock); auto iterator = _registeredDevices.find(deviceID); - return iterator->second->getAvailabeInputs(); + return iterator->second->getAvailableInputs(); } QVector UserInputMapper::getAllActions() const { @@ -336,7 +329,7 @@ void UserInputMapper::assignDefaulActionScales() { static int actionMetaTypeId = qRegisterMetaType(); static int inputMetaTypeId = qRegisterMetaType(); -static int inputPairMetaTypeId = qRegisterMetaType(); +static int inputPairMetaTypeId = qRegisterMetaType(); static int poseMetaTypeId = qRegisterMetaType("Pose"); @@ -344,8 +337,8 @@ QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); void inputFromScriptValue(const QScriptValue& object, Input& input); QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action); void actionFromScriptValue(const QScriptValue& object, Action& action); -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const InputPair& inputPair); -void inputPairFromScriptValue(const QScriptValue& object, InputPair& inputPair); +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair); +void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair); QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) { QScriptValue obj = engine->newObject(); @@ -372,21 +365,21 @@ void actionFromScriptValue(const QScriptValue& object, Action& action) { action = Action(object.property("action").toVariant().toInt()); } -QScriptValue inputPairToScriptValue(QScriptEngine* engine, const InputPair& inputPair) { +QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair) { QScriptValue obj = engine->newObject(); obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); obj.setProperty("inputName", inputPair.second); return obj; } -void inputPairFromScriptValue(const QScriptValue& object, InputPair& inputPair) { +void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair) { inputFromScriptValue(object.property("input"), inputPair.first); inputPair.second = QString(object.property("inputName").toVariant().toString()); } void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { qScriptRegisterSequenceMetaType >(engine); - qScriptRegisterSequenceMetaType >(engine); + qScriptRegisterSequenceMetaType(engine); qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index e28fbd740d..884e303fc6 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -42,7 +42,6 @@ namespace controller { Q_ENUMS(Action) public: - using InputPair = Input::NamedPair; // FIXME move to unordered set / map using EndpointToInputMap = std::map; using MappingNameMap = std::map; @@ -52,7 +51,7 @@ namespace controller { using EndpointSet = std::unordered_set; using EndpointPair = std::pair; using EndpointPairMap = std::map; - using DevicesMap = std::map; + using DevicesMap = std::map; using uint16 = uint16_t; using uint32 = uint32_t; @@ -65,9 +64,8 @@ namespace controller { static void registerControllerTypes(QScriptEngine* engine); - - void registerDevice(InputDevice* device); - DeviceProxy::Pointer getDeviceProxy(const Input& input); + void registerDevice(InputDevice::Pointer device); + InputDevice::Pointer getDevice(const Input& input); QString getDeviceName(uint16 deviceID); Input::NamedVector getAvailableInputs(uint16 deviceID) const; @@ -104,7 +102,7 @@ namespace controller { DevicesMap getDevices() { return _registeredDevices; } uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } - DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } + InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } MappingPointer newMapping(const QString& mappingName); MappingPointer parseMapping(const QString& json); @@ -122,7 +120,6 @@ namespace controller { protected: // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } - InputDevice::Pointer _standardController; DevicesMap _registeredDevices; uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; @@ -184,9 +181,9 @@ namespace controller { } -Q_DECLARE_METATYPE(controller::UserInputMapper::InputPair) +Q_DECLARE_METATYPE(controller::Input::NamedPair) Q_DECLARE_METATYPE(controller::Pose) -Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(controller::Input) Q_DECLARE_METATYPE(controller::Action) Q_DECLARE_METATYPE(QVector) diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp index 32cd5b65e0..bb1f6df191 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp @@ -19,11 +19,11 @@ float InputEndpoint::value(){ return pose().valid ? 1.0f : 0.0f; } auto userInputMapper = DependencyManager::get(); - auto deviceProxy = userInputMapper->getDeviceProxy(_input); + auto deviceProxy = userInputMapper->getDevice(_input); if (!deviceProxy) { return 0.0f; } - return deviceProxy->getValue(_input, 0); + return deviceProxy->getValue(_input); } Pose InputEndpoint::pose() { @@ -32,10 +32,10 @@ Pose InputEndpoint::pose() { return Pose(); } auto userInputMapper = DependencyManager::get(); - auto deviceProxy = userInputMapper->getDeviceProxy(_input); + auto deviceProxy = userInputMapper->getDevice(_input); if (!deviceProxy) { return Pose(); } - return deviceProxy->getPose(_input, 0); + return deviceProxy->getPose(_input.channel); } diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 4c33b2517a..d994a1f5aa 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME input-plugins) setup_hifi_library() -link_hifi_libraries(shared plugins controllers) +link_hifi_libraries(shared plugins controllers render-utils) GroupSources("src/input-plugins") diff --git a/libraries/input-plugins/src/input-plugins/Joystick.cpp b/libraries/input-plugins/src/input-plugins/Joystick.cpp index 30074b37d3..b7d69b9406 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.cpp +++ b/libraries/input-plugins/src/input-plugins/Joystick.cpp @@ -71,63 +71,58 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { #endif -void Joystick::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { +controller::Input::NamedVector Joystick::getAvailableInputs() const { using namespace controller; - proxy->_name = _name; - proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this](const Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs{ - makePair(A, "A"), - makePair(B, "B"), - makePair(X, "X"), - makePair(Y, "Y"), - // DPad - makePair(DU, "DU"), - makePair(DD, "DD"), - makePair(DL, "DL"), - makePair(DR, "DR"), - // Bumpers - makePair(LB, "LB"), - makePair(RB, "RB"), - // Stick press - makePair(LS, "LS"), - makePair(RS, "RS"), - // Center buttons - makePair(START, "Start"), - makePair(BACK, "Back"), - // Analog sticks - makePair(LX, "LX"), - makePair(LY, "LY"), - makePair(RX, "RX"), - makePair(RY, "RY"), + static const Input::NamedVector availableInputs{ + makePair(A, "A"), + makePair(B, "B"), + makePair(X, "X"), + makePair(Y, "Y"), + // DPad + makePair(DU, "DU"), + makePair(DD, "DD"), + makePair(DL, "DL"), + makePair(DR, "DR"), + // Bumpers + makePair(LB, "LB"), + makePair(RB, "RB"), + // Stick press + makePair(LS, "LS"), + makePair(RS, "RS"), + // Center buttons + makePair(START, "Start"), + makePair(BACK, "Back"), + // Analog sticks + makePair(LX, "LX"), + makePair(LY, "LY"), + makePair(RX, "RX"), + makePair(RY, "RY"), - // Triggers - makePair(LT, "LT"), - makePair(RT, "RT"), + // Triggers + makePair(LT, "LT"), + makePair(RT, "RT"), - // Aliases, PlayStation style names - makePair(LB, "L1"), - makePair(RB, "R1"), - makePair(LT, "L2"), - makePair(RT, "R2"), - makePair(LS, "L3"), - makePair(RS, "R3"), - makePair(BACK, "Select"), - makePair(A, "Cross"), - makePair(B, "Circle"), - makePair(X, "Square"), - makePair(Y, "Triangle"), - makePair(DU, "Up"), - makePair(DD, "Down"), - makePair(DL, "Left"), - makePair(DR, "Right"), - }; - return availableInputs; + // Aliases, PlayStation style names + makePair(LB, "L1"), + makePair(RB, "R1"), + makePair(LT, "L2"), + makePair(RT, "R2"), + makePair(LS, "L3"), + makePair(RS, "R3"), + makePair(BACK, "Select"), + makePair(A, "Cross"), + makePair(B, "Circle"), + makePair(X, "Square"), + makePair(Y, "Triangle"), + makePair(DU, "Up"), + makePair(DD, "Down"), + makePair(DL, "Left"), + makePair(DR, "Right"), }; + return availableInputs; } -QString Joystick::getDefaultMappingConfig() { +QString Joystick::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/xbox.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/Joystick.h b/libraries/input-plugins/src/input-plugins/Joystick.h index a9ed18607c..fa50f8eab6 100644 --- a/libraries/input-plugins/src/input-plugins/Joystick.h +++ b/libraries/input-plugins/src/input-plugins/Joystick.h @@ -32,12 +32,13 @@ class Joystick : public QObject, public controller::InputDevice { #endif public: + using Pointer = std::shared_ptr; const QString& getName() const { return _name; } // Device functions - virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override; + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index ff6c0ce2de..a0481dfaa0 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -130,7 +130,7 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { _lastTouch = currentPos; } -controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) { +controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { auto shortCode = (uint16_t)(code & KEYBOARD_MASK); if (shortCode != code) { shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys @@ -138,7 +138,7 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) { +controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const { switch (code) { case Qt::LeftButton: return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); @@ -151,31 +151,30 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) { }; } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) { +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) { +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) { +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); } -void KeyboardMouseDevice::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { +controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { using namespace controller; - proxy->getButton = [this] (const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this] (const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getAvailabeInputs = [this] () -> QVector { - QVector availableInputs; - for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { + static QVector availableInputs; + static std::once_flag once; + std::call_once(once, [&] { + for (int i = (int)Qt::Key_0; i <= (int)Qt::Key_9; i++) { availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } - for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) { + for (int i = (int)Qt::Key_A; i <= (int)Qt::Key_Z; i++) { availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } - for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) { + for (int i = (int)Qt::Key_Left; i <= (int)Qt::Key_Down; i++) { availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); } availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); @@ -186,27 +185,26 @@ void KeyboardMouseDevice::buildDeviceProxy(controller::DeviceProxy::Pointer prox availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick")); availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick")); - + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown")); - + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "MouseWheelDown")); - + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "TouchpadRight")); availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft")); availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp")); availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown")); - - return availableInputs; - }; + }); + return availableInputs; } -QString KeyboardMouseDevice::getDefaultMappingConfig() { +QString KeyboardMouseDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index f89d877dcd..1ff77d2dce 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -72,8 +72,8 @@ public: virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } // Device functions - virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override; + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; @@ -91,11 +91,11 @@ public: void wheelEvent(QWheelEvent* event); // Let's make it easy for Qt because we assume we love Qt forever - controller::Input makeInput(Qt::Key code); - controller::Input makeInput(Qt::MouseButton code); - controller::Input makeInput(MouseAxisChannel axis); - controller::Input makeInput(TouchAxisChannel axis); - controller::Input makeInput(TouchButtonChannel button); + controller::Input makeInput(Qt::Key code) const; + controller::Input makeInput(Qt::MouseButton code) const; + controller::Input makeInput(MouseAxisChannel axis) const; + controller::Input makeInput(TouchAxisChannel axis) const; + controller::Input makeInput(TouchButtonChannel button) const; static const QString NAME; diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index 54197b1a70..d021e35a54 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -49,11 +49,11 @@ void SDL2Manager::init() { SDL_JoystickID id = getInstanceId(controller); if (!_openJoysticks.contains(id)) { //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); - Joystick* joystick = new Joystick(id, controller); + Joystick::Pointer joystick = std::make_shared(id, controller); _openJoysticks[id] = joystick; auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(joystick); - emit joystickAdded(joystick); + emit joystickAdded(joystick.get()); } } } @@ -68,7 +68,7 @@ void SDL2Manager::init() { void SDL2Manager::deinit() { #ifdef HAVE_SDL2 - qDeleteAll(_openJoysticks); + _openJoysticks.clear(); SDL_Quit(); #endif @@ -103,12 +103,12 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_CONTROLLERAXISMOTION) { - Joystick* joystick = _openJoysticks[event.caxis.which]; + Joystick::Pointer joystick = _openJoysticks[event.caxis.which]; if (joystick) { joystick->handleAxisEvent(event.caxis); } } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) { - Joystick* joystick = _openJoysticks[event.cbutton.which]; + Joystick::Pointer joystick = _openJoysticks[event.cbutton.which]; if (joystick) { joystick->handleButtonEvent(event.cbutton); } @@ -128,16 +128,18 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { SDL_JoystickID id = getInstanceId(controller); if (!_openJoysticks.contains(id)) { // Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); - Joystick* joystick = new Joystick(id, controller); + Joystick::Pointer joystick = std::make_shared(id, controller); _openJoysticks[id] = joystick; userInputMapper->registerDevice(joystick); - emit joystickAdded(joystick); + emit joystickAdded(joystick.get()); } } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { - Joystick* joystick = _openJoysticks[event.cdevice.which]; - _openJoysticks.remove(event.cdevice.which); - userInputMapper->removeDevice(joystick->getDeviceID()); - emit joystickRemoved(joystick); + if (_openJoysticks.contains(event.cdevice.which)) { + Joystick::Pointer joystick = _openJoysticks[event.cdevice.which]; + _openJoysticks.remove(event.cdevice.which); + userInputMapper->removeDevice(joystick->getDeviceID()); + emit joystickRemoved(joystick.get()); + } } } } diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/libraries/input-plugins/src/input-plugins/SDL2Manager.h index fec6972591..4cf9cd33d8 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.h @@ -76,7 +76,7 @@ private: int buttonPressed() const { return SDL_PRESSED; } int buttonRelease() const { return SDL_RELEASED; } - QMap _openJoysticks; + QMap _openJoysticks; #endif bool _isInitialized; static const QString NAME; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index d5ff4c93a8..9ef1599099 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -64,11 +64,12 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; const float DEFAULT_REACH_LENGTH = 1.5f; - +static std::shared_ptr instance; SixenseManager::SixenseManager() : InputDevice("Hydra"), _reachLength(DEFAULT_REACH_LENGTH) { + instance = std::shared_ptr(this); } bool SixenseManager::isSupported() const { @@ -91,7 +92,7 @@ void SixenseManager::activate() { true, true); auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(this); + userInputMapper->registerDevice(instance); #ifdef __APPLE__ @@ -512,42 +513,37 @@ static const auto R4 = controller::Y; using namespace controller; -void SixenseManager::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { - proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this](const Input& input, int timestamp) -> float { - return this->getAxis(input.getChannel()); +controller::Input::NamedVector SixenseManager::getAvailableInputs() const { + using namespace controller; + static const Input::NamedVector availableInputs { + makePair(L0, "L0"), + makePair(L1, "L1"), + makePair(L2, "L2"), + makePair(L3, "L3"), + makePair(L4, "L4"), + makePair(LB, "LB"), + makePair(LS, "LS"), + makePair(LX, "LX"), + makePair(LY, "LY"), + makePair(LT, "LT"), + makePair(R0, "R0"), + makePair(R1, "R1"), + makePair(R2, "R2"), + makePair(R3, "R3"), + makePair(R4, "R4"), + makePair(RB, "RB"), + makePair(RS, "RS"), + makePair(RX, "RX"), + makePair(RY, "RY"), + makePair(RT, "RT"), + makePair(LEFT_HAND, "LeftHand"), + makePair(RIGHT_HAND, "RightHand"), }; - proxy->getPose = [this](const Input& input, int timestamp) -> Pose { return this->getPose(input.getChannel()); }; - proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs; - availableInputs.append(Input::NamedPair(makeInput(L0), "L0")); - availableInputs.append(Input::NamedPair(makeInput(L1), "L1")); - availableInputs.append(Input::NamedPair(makeInput(L2), "L2")); - availableInputs.append(Input::NamedPair(makeInput(L3), "L3")); - availableInputs.append(Input::NamedPair(makeInput(L4), "L4")); - availableInputs.append(Input::NamedPair(makeInput(LB), "LB")); - availableInputs.append(Input::NamedPair(makeInput(LS), "LS")); - availableInputs.append(Input::NamedPair(makeInput(LX), "LX")); - availableInputs.append(Input::NamedPair(makeInput(LY), "LY")); - availableInputs.append(Input::NamedPair(makeInput(LT), "LT")); - availableInputs.append(Input::NamedPair(makeInput(R0), "R0")); - availableInputs.append(Input::NamedPair(makeInput(R1), "R1")); - availableInputs.append(Input::NamedPair(makeInput(R2), "R2")); - availableInputs.append(Input::NamedPair(makeInput(R3), "R3")); - availableInputs.append(Input::NamedPair(makeInput(R4), "R4")); - availableInputs.append(Input::NamedPair(makeInput(RB), "RB")); - availableInputs.append(Input::NamedPair(makeInput(RS), "RS")); - availableInputs.append(Input::NamedPair(makeInput(RX), "RX")); - availableInputs.append(Input::NamedPair(makeInput(RY), "RY")); - availableInputs.append(Input::NamedPair(makeInput(RT), "RT")); - availableInputs.append(Input::NamedPair(makeInput(LEFT_HAND), "LeftHand")); - availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "RightHand")); - return availableInputs; - }; -} + return availableInputs; +}; -QString SixenseManager::getDefaultMappingConfig() { +QString SixenseManager::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index a44f527238..5b5cb7ccfa 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -62,8 +62,8 @@ public: virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } // Device functions - virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override; + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 8dd3d21a07..02d27d7e05 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -44,6 +44,8 @@ const QString MENU_NAME = "Vive Controllers"; const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString RENDER_CONTROLLERS = "Render Hand Controllers"; +static std::shared_ptr instance; + ViveControllerManager::ViveControllerManager() : InputDevice("Vive"), _trackedControllers(0), @@ -52,7 +54,7 @@ ViveControllerManager::ViveControllerManager() : _rightHandRenderID(0), _renderControllers(false) { - + instance = std::shared_ptr(this); } bool ViveControllerManager::isSupported() const { @@ -278,7 +280,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } if (_trackedControllers == 0 && numTrackedControllers > 0) { - userInputMapper->registerDevice(this); + userInputMapper->registerDevice(instance); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); } @@ -392,62 +394,43 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation); } -void ViveControllerManager::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { +controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const { using namespace controller; - proxy->_name = _name; - proxy->getButton = [this](const Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; - proxy->getAxis = [this](const Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; - proxy->getPose = [this](const Input& input, int timestamp) -> Pose { return this->getPose(input.getChannel()); }; - proxy->getAvailabeInputs = [this]() -> QVector { - QVector availableInputs{ - // Trackpad analogs - makePair(LX, "LX"), - makePair(LY, "LY"), - makePair(RX, "RX"), - makePair(RY, "RY"), - // trigger analogs - makePair(LT, "LT"), - makePair(RT, "RT"), + QVector availableInputs{ + // Trackpad analogs + makePair(LX, "LX"), + makePair(LY, "LY"), + makePair(RX, "RX"), + makePair(RY, "RY"), + // trigger analogs + makePair(LT, "LT"), + makePair(RT, "RT"), - makePair(LB, "LB"), - makePair(RB, "RB"), + makePair(LB, "LB"), + makePair(RB, "RB"), - makePair(LS, "LS"), - makePair(RS, "RS"), - makePair(LEFT_HAND, "LeftHand"), - makePair(RIGHT_HAND, "RightHand"), - }; - - //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); - //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); - //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); - //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); - - //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up")); - //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down")); - //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right")); - //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left")); - //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); - - - //availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand")); - - //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A")); - //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); - //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); - //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); - - //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up")); - //availableInputs.append(Input::NamedPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down")); - //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right")); - //availableInputs.append(Input::NamedPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left")); - //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger")); - - return availableInputs; + makePair(LS, "LS"), + makePair(RS, "RS"), + makePair(LEFT_HAND, "LeftHand"), + makePair(RIGHT_HAND, "RightHand"), }; + + //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); + //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); + //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); + //availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand")); + //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A")); + //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); + //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); + //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger")); + + return availableInputs; } -QString ViveControllerManager::getDefaultMappingConfig() { +QString ViveControllerManager::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; return MAPPING_JSON; } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index 67ad75c9e8..dc04398b20 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -41,8 +41,8 @@ public: virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } // Device functions - virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; - virtual QString getDefaultMappingConfig() override; + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; virtual void update(float deltaTime, bool jointsCaptured) override; virtual void focusOutEvent() override; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index bc0ad179ef..a7b1be15ca 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -136,7 +136,7 @@ int main(int argc, char** argv) { auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky - userInputMapper->registerDevice(keyboardMouseDevice); + userInputMapper->registerDevice(std::shared_ptr(keyboardMouseDevice)); } inputPlugin->pluginUpdate(0, false); } From a3cd032a41b39d38a26460acfec39561fde2ba6e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 10:06:21 -0700 Subject: [PATCH 197/232] register/remove devices when the input plugins are activated/deactivated --- examples/controllers/controllerMappings.js | 8 +++++++- .../src/input-plugins/SDL2Manager.cpp | 17 +++++++++++++++++ .../src/input-plugins/SDL2Manager.h | 7 ++++++- .../src/input-plugins/SixenseManager.cpp | 4 +++- .../src/input-plugins/ViveControllerManager.cpp | 15 +++++++++++++-- .../src/input-plugins/ViveControllerManager.h | 3 +++ 6 files changed, 49 insertions(+), 5 deletions(-) diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index 42494816f4..3848f62096 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -91,5 +91,11 @@ Object.keys(Controller.Actions).forEach(function (actionName) { Controller.hardwareChanged.connect(function () { - print("hardwareChanged"); + print("hardwareChanged ---------------------------------------------------"); + Object.keys(Controller.Hardware).forEach(function (deviceName) { + Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { + print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); + }); + }); + print("-------------------------------------------------------------------"); }); \ No newline at end of file diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index 54197b1a70..554b0bac95 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -74,6 +74,23 @@ void SDL2Manager::deinit() { #endif } +void SDL2Manager::activate() { + auto userInputMapper = DependencyManager::get(); + for (auto joystick : _openJoysticks) { + userInputMapper->registerDevice(joystick); + emit joystickAdded(joystick); + } +} + +void SDL2Manager::deactivate() { + auto userInputMapper = DependencyManager::get(); + for (auto joystick : _openJoysticks) { + userInputMapper->removeDevice(joystick->getDeviceID()); + emit joystickRemoved(joystick); + } +} + + bool SDL2Manager::isSupported() const { #ifdef HAVE_SDL2 return true; diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.h b/libraries/input-plugins/src/input-plugins/SDL2Manager.h index fec6972591..ed543d4265 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.h +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.h @@ -34,7 +34,12 @@ public: virtual void init() override; virtual void deinit() override; - + + /// Called when a plugin is being activated for use. May be called multiple times. + virtual void activate() override; + /// Called when a plugin is no longer being used. May be called multiple times. + virtual void deactivate() override; + virtual void pluginFocusOutEvent() override; virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index d5ff4c93a8..60138929e9 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -92,6 +92,8 @@ void SixenseManager::activate() { auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(this); + qDebug() << "just called registerDevice hydra id:" << _deviceID; + #ifdef __APPLE__ @@ -125,6 +127,7 @@ void SixenseManager::activate() { void SixenseManager::deactivate() { InputPlugin::deactivate(); + #ifdef HAVE_SIXENSE CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); CONTAINER->removeMenu(MENU_PATH); @@ -135,7 +138,6 @@ void SixenseManager::deactivate() { if (_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); userInputMapper->removeDevice(_deviceID); - _deviceID = controller::Input::INVALID_DEVICE; } #ifdef __APPLE__ diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 8dd3d21a07..dc3e062417 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -133,6 +133,11 @@ void ViveControllerManager::activate() { _renderControllers = true; } #endif + + // unregister with UserInputMapper + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(this); + _registeredWithInputMapper = true; } void ViveControllerManager::deactivate() { @@ -150,6 +155,11 @@ void ViveControllerManager::deactivate() { } _poseStateMap.clear(); #endif + + // unregister with UserInputMapper + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(_deviceID); + _registeredWithInputMapper = false; } void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) { @@ -270,15 +280,16 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { auto userInputMapper = DependencyManager::get(); if (numTrackedControllers == 0) { - if (_deviceID != 0) { + if (_registeredWithInputMapper) { userInputMapper->removeDevice(_deviceID); - _deviceID = 0; + _registeredWithInputMapper = false; _poseStateMap.clear(); } } if (_trackedControllers == 0 && numTrackedControllers > 0) { userInputMapper->registerDevice(this); + _registeredWithInputMapper = true; UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index 67ad75c9e8..6984734c21 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -69,6 +69,9 @@ private: bool _renderControllers; static const QString NAME; + + bool _registeredWithInputMapper { false }; + }; #endif // hifi__ViveControllerManager From 4083c5c71b848d300ef73709003a119e06dbe4a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 10:31:36 -0700 Subject: [PATCH 198/232] Handle wrapping of very long-lived sessions. --- libraries/animation/src/Rig.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index de2d3c54ad..b51907ea4a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -580,18 +580,20 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // Allow script to add/remove handlers and report results, from within their thread. QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread QMutexLocker locker(&_stateMutex); - int identifier = ++_nextStateHandlerId; // 0 is unused - StateHandler& data = _stateHandlers[identifier]; + while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap. + _nextStateHandlerId++; + } + StateHandler& data = _stateHandlers[_nextStateHandlerId]; data.function = handler; data.useNames = propertiesList.isArray(); if (data.useNames) { data.propertyNames = propertiesList.toVariant().toStringList(); } - return QScriptValue(identifier); // suitable for giving to removeAnimationStateHandler + return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler } void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread QMutexLocker locker(&_stateMutex); - _stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present + _stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present. 0 is unused } void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script QMutexLocker locker(&_stateMutex); From ed87ae3d5a4461dacdef532188af3763293c205e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 10:57:30 -0700 Subject: [PATCH 199/232] remove some debugging --- libraries/input-plugins/src/input-plugins/SixenseManager.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 31a7f50d5c..2527da9e03 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -81,7 +81,6 @@ bool SixenseManager::isSupported() const { } void SixenseManager::activate() { - qDebug() << "SixenseManager::activate()..."; InputPlugin::activate(); #ifdef HAVE_SIXENSE _calibrationState = CALIBRATION_STATE_IDLE; @@ -126,7 +125,6 @@ void SixenseManager::activate() { } void SixenseManager::deactivate() { - qDebug() << "SixenseManager::deactivate()..."; InputPlugin::deactivate(); #ifdef HAVE_SIXENSE From 3c6d4f9c221ac19c8f3b12070ee396c52f72bec1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 11:02:24 -0700 Subject: [PATCH 200/232] Thread safety per #6154. --- interface/src/avatar/AvatarManager.cpp | 10 ++++++++++ interface/src/avatar/AvatarManager.h | 6 ++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 9783590b05..fbfbbad2de 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -248,6 +248,16 @@ QVector AvatarManager::getLocalLights() const { return _localLights; } +QVector AvatarManager::getAvatarIdentifiers() { + QReadLocker locker(&_hashLock); + return _avatarHash.keys().toVector(); +} +AvatarData* AvatarManager::getAvatar(QUuid avatarID) { + QReadLocker locker(&_hashLock); + return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar. +} + + void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) { result.clear(); result.swap(_motionStatesToDelete); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ffc7cc8f92..fa0593368b 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -53,10 +53,8 @@ public: Q_INVOKABLE void setLocalLights(const QVector& localLights); Q_INVOKABLE QVector getLocalLights() const; // Currently, your own avatar will be included as the null avatar id. - Q_INVOKABLE QVector getAvatarIdentifiers() const { return _avatarHash.keys().toVector(); } // FIXME: see #6154 - Q_INVOKABLE QVector getAvatars() const { return getAvatarIdentifiers(); } // FIXME: remove before merge. Compatability for testing scripts. - // Minor Bug: A bogus avatarID answers your own avatar. - Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID) const { return _avatarHash[avatarID].get(); } // FIXME: see #6154 + Q_INVOKABLE QVector getAvatarIdentifiers(); + Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID); void getObjectsToDelete(VectorOfMotionStates& motionStates); From 3d37a1d2497dba09d1a03bf54a1c5d8d5fc6e170 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 11:06:20 -0700 Subject: [PATCH 201/232] fix Controller.Hardware when input plugins are deactived --- .../controllers/src/controllers/ScriptingInterface.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index e49248e8f0..a62172a730 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -204,25 +204,24 @@ namespace controller { } void ScriptingInterface::updateMaps() { + QVariantMap newHardware; auto userInputMapper = DependencyManager::get(); auto devices = userInputMapper->getDevices(); - QSet foundDevices; for (const auto& deviceMapping : devices) { auto deviceID = deviceMapping.first; if (deviceID != userInputMapper->getStandardDeviceID()) { auto device = deviceMapping.second; auto deviceName = QString(device->getName()).remove(SANITIZE_NAME_EXPRESSION); qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; - foundDevices.insert(device->getName()); - if (_hardware.contains(deviceName)) { + if (newHardware.contains(deviceName)) { continue; } // Expose the IDs to JS - _hardware.insert(deviceName, createDeviceMap(device)); + newHardware.insert(deviceName, createDeviceMap(device)); } - } + _hardware = newHardware; } From 851460b2bd973e42d8c1f46b3346000c36013ce5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 11:26:11 -0700 Subject: [PATCH 202/232] fix build buster for SDL2 missing builds --- libraries/input-plugins/src/input-plugins/SDL2Manager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index f41b724c11..600dc5c56f 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -75,19 +75,23 @@ void SDL2Manager::deinit() { } void SDL2Manager::activate() { +#ifdef HAVE_SDL2 auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { userInputMapper->registerDevice(joystick); emit joystickAdded(joystick.get()); } +#endif } void SDL2Manager::deactivate() { +#ifdef HAVE_SDL2 auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { userInputMapper->removeDevice(joystick->getDeviceID()); emit joystickRemoved(joystick.get()); } +#endif } From 8f908f987779a33a7f13b6d7e32d8892567fa03f Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 12:08:11 -0700 Subject: [PATCH 203/232] Adding the stepYaw to the Standard mapping --- interface/resources/controllers/standard.json | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 8ba9056076..4fe6dba923 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,6 +3,7 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, + { "from": "Standard.RX", "when": "Application.InHMD", "to": "Actions.StepYaw" }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, From e902e5e97a725f569ab7cc344373c8197b5d8ca2 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 13:05:29 -0700 Subject: [PATCH 204/232] Introduce the concept of loading the default Mapping --- interface/src/Application.cpp | 2 ++ .../src/controllers/UserInputMapper.cpp | 23 +++++++++++++++++++ .../src/controllers/UserInputMapper.h | 1 + 3 files changed, 26 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e4b4010f0..7d2929d31e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -648,6 +648,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : userInputMapper->registerDevice(_keyboardMouseDevice); + userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); + // check first run... if (_firstRun.get()) { qCDebug(interfaceapp) << "This is a first run..."; diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 64a2a54ef4..736fa30d37 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -129,6 +129,27 @@ void UserInputMapper::removeDevice(int deviceID) { } +void UserInputMapper::loadDefaultMapping(uint16 deviceID) { + Locker locker(_lock); + auto proxyEntry = _registeredDevices.find(deviceID); + if (_registeredDevices.end() == proxyEntry) { + qCWarning(controllers) << "Unknown deviceID " << deviceID; + return; + } + + + auto mapping = loadMapping(proxyEntry->second->getDefaultMappingConfig()); + if (mapping) { + auto prevMapping = _mappingsByDevice[deviceID]; + disableMapping(prevMapping); + + _mappingsByDevice[deviceID] = mapping; + enableMapping(mapping); + } + + emit hardwareChanged(); +} + InputDevice::Pointer UserInputMapper::getDevice(const Input& input) { Locker locker(_lock); auto device = _registeredDevices.find(input.getDevice()); @@ -711,6 +732,8 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { return parseMapping(json); } + + static const QString JSON_NAME = QStringLiteral("name"); static const QString JSON_CHANNELS = QStringLiteral("channels"); static const QString JSON_CHANNEL_FROM = QStringLiteral("from"); diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 884e303fc6..a32c3f3649 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -108,6 +108,7 @@ namespace controller { MappingPointer parseMapping(const QString& json); MappingPointer loadMapping(const QString& jsonFile); + void loadDefaultMapping(uint16 deviceID); void enableMapping(const QString& mappingName, bool enable = true); float getValue(const Input& input) const; Pose getPose(const Input& input) const; From ffd2b39874d1c40447ab89bc5d5c95e1839f1c62 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 29 Oct 2015 13:18:37 -0700 Subject: [PATCH 205/232] Fix vive controller - update to new input plugin register --- interface/resources/controllers/vive.json | 5 ++++- .../src/input-plugins/ViveControllerManager.cpp | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index f51f908813..bcf4943ff8 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -17,6 +17,9 @@ { "from": "Vive.Start", "to": "Standard.Start" }, { "from": "Vive.A", "to": "Standard.A" }, - { "from": "Vive.B", "to": "Standard.B" } + { "from": "Vive.B", "to": "Standard.B" }, + + { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, + { "from": "Vive.RightHand", "to": "Standard.RightHand" } ] } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 6d1534958a..c63d47b681 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -424,6 +424,11 @@ controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const makePair(RS, "RS"), makePair(LEFT_HAND, "LeftHand"), makePair(RIGHT_HAND, "RightHand"), + + makePair(A, "A"), + makePair(B, "B"), + makePair(BACK, "Back"), + makePair(START, "Start"), }; //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); From d73eafddd1a0e2511446463fc7e0190349c0e8bd Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 13:48:16 -0700 Subject: [PATCH 206/232] COmfort mode working with the COntroller system observing the menu state --- interface/resources/controllers/standard.json | 2 +- interface/src/Application.cpp | 11 +++++++---- .../controllers/src/controllers/StateController.cpp | 4 ++++ .../controllers/src/controllers/StateController.h | 3 +++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 4fe6dba923..5483da925d 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,7 +3,7 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", "when": "Application.InHMD", "to": "Actions.StepYaw" }, + { "from": "Standard.RX", "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw" }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7d2929d31e..118d34230c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -637,10 +637,13 @@ 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(); - auto InHMDLambda = controller::StateController::ReadLambda([]() -> float { - return (float) qApp->getAvatarUpdater()->isHMDMode(); - }); - _applicationStateDevice->addInputVariant("InHMD", InHMDLambda); + + _applicationStateDevice->addInputVariant("InHMD", controller::StateController::ReadLambda([]() -> float { + return (float)qApp->getAvatarUpdater()->isHMDMode(); + })); + _applicationStateDevice->addInputVariant("ComfortMode", controller::StateController::ReadLambda([]() -> float { + return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); + })); userInputMapper->registerDevice(_applicationStateDevice); diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp index efe7a064fc..9d2f3baf86 100644 --- a/libraries/controllers/src/controllers/StateController.cpp +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -43,4 +43,8 @@ Input::NamedVector StateController::getAvailableInputs() const { return availableInputs; } +EndpointPointer StateController::createEndpoint(const Input& input) const { + return std::make_shared(_namedReadLambdas[input.getChannel()].second); +} + } \ No newline at end of file diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index d664c6b8d0..7a4c386c5e 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -39,6 +39,9 @@ public: void addInputVariant(QString name, ReadLambda& lambda); + virtual EndpointPointer createEndpoint(const Input& input) const override; + + protected: QVector _namedReadLambdas; }; From f72146c35d224bffedea7a6d28195966173d14a1 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 14:17:52 -0700 Subject: [PATCH 207/232] FIxing the mac build --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 118d34230c..7f4b5a3c3d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -638,10 +638,10 @@ 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->addInputVariant("InHMD", controller::StateController::ReadLambda([]() -> float { + _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float { return (float)qApp->getAvatarUpdater()->isHMDMode(); })); - _applicationStateDevice->addInputVariant("ComfortMode", controller::StateController::ReadLambda([]() -> float { + _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); })); From c1e00ca08c81fb1e3391e10f3e4e670dd6645680 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 14:30:51 -0700 Subject: [PATCH 208/232] FIxing the mac build again ? --- libraries/controllers/src/controllers/StateController.cpp | 2 +- libraries/controllers/src/controllers/StateController.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp index 9d2f3baf86..6f89c6365c 100644 --- a/libraries/controllers/src/controllers/StateController.cpp +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -29,7 +29,7 @@ void StateController::update(float deltaTime, bool jointsCaptured) {} void StateController::focusOutEvent() {} -void StateController::addInputVariant(QString name, ReadLambda& lambda) { +void StateController::addInputVariant(QString name, ReadLambda lambda) { _namedReadLambdas.push_back(NamedReadLambda(name, lambda)); } diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index 7a4c386c5e..12f3e8b2f1 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -37,7 +37,7 @@ public: using ReadLambda = std::function; using NamedReadLambda = QPair; - void addInputVariant(QString name, ReadLambda& lambda); + void addInputVariant(QString name, ReadLambda lambda); virtual EndpointPointer createEndpoint(const Input& input) const override; From b070306c2c002bd08662fcc4c7c3b860bd5fc4cc Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 15:53:17 -0700 Subject: [PATCH 209/232] tweak comfort mode filters --- .../resources/controllers/keyboardMouse.json | 42 +++++++++++++++++++ interface/resources/controllers/standard.json | 16 ++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 6ea1c42b1e..fba55ffebd 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -1,6 +1,47 @@ { "name": "Keyboard/Mouse to Actions", "channels": [ + + { "from": ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], + "when": [ "Application.InHMD", "Application.ComfortMode" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5 }, + { "type": "scale", "scale": -15 } + ] + }, + + { "from": ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"], + "when": [ "Application.InHMD", "Application.ComfortMode" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5 }, + { "type": "scale", "scale": 15 } + ] + }, + + { "from": "Keyboard.MouseMoveLeft", + "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5 }, + { "type": "scale", "scale": -15 } + ] + }, + + { "from": "Keyboard.MouseMoveRight", + "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5 }, + { "type": "scale", "scale": 15 } + ] + }, + { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, @@ -28,6 +69,7 @@ { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" }, { "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5483da925d..871374b85b 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -3,10 +3,22 @@ "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw" }, + + { "from": "Standard.RX", + "when": [ "Application.InHMD", "Application.ComfortMode" ], + "to": "Actions.StepYaw", + "filters": + [ + { "type": "pulse", "interval": 0.5 }, + { "type": "scale", "scale": 15 } + ] + }, + + { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "to": "Actions.Pitch" }, + { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, @@ -20,3 +32,5 @@ { "from": "Standard.RightHand", "to": "Actions.RightHand" } ] } + + From 303491817bd14dd17e2ab6eaa95674145c125bca Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 16:35:59 -0700 Subject: [PATCH 210/232] assert to get hard error in dev, warning and no-op in release. --- libraries/animation/src/AnimVariantMap.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index 0c808bd404..fc474c0cbd 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -18,6 +18,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { if (QThread::currentThread() != engine->thread()) { qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); + Q_ASSERT(false); return QScriptValue(); } QScriptValue target = engine->newObject(); @@ -69,6 +70,7 @@ void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { if (QThread::currentThread() != source.engine()->thread()) { qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread(); + Q_ASSERT(false); return; } // POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types. From 502cc7f580c90483b65d10638f2e622a40c8c702 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 16:44:01 -0700 Subject: [PATCH 211/232] Don't copy while converting. --- libraries/animation/src/AnimVariantMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index fc474c0cbd..2d9291db2d 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -22,7 +22,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, return QScriptValue(); } QScriptValue target = engine->newObject(); - auto setOne = [&] (QString name, AnimVariant value) { + auto setOne = [&] (const QString& name, const AnimVariant& value) { switch (value.getType()) { case AnimVariant::Type::Bool: target.setProperty(name, value.getBool()); From b56c49a182154c66dd20720825e0ea55e7d3713f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 17:33:30 -0700 Subject: [PATCH 212/232] add makeAxis support to JSON parsing --- .../src/controllers/UserInputMapper.cpp | 43 ++++++++++++++++++- .../src/controllers/UserInputMapper.h | 2 + 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 736fa30d37..d7f57d91e3 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -747,7 +747,15 @@ Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { if (value.isString()) { auto input = findDeviceInput(value.toString()); result = endpointFor(input); + } else if (value.isArray()) { + return parseAny(value); } else if (value.isObject()) { + auto axisEndpoint = parseAxis(value); + if (axisEndpoint) { + return axisEndpoint; + } + // if we have other types of endpoints that are objects, follow the axisEndpoint example, and place them here + // Endpoint is defined as an object, we expect a js function then return Endpoint::Pointer(); } @@ -881,7 +889,28 @@ Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { return parseEndpoint(value); } -Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) { +Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { + if (value.isObject()) { + auto object = value.toObject(); + if (object.contains("makeAxis")) { + auto axisValue = object.value("makeAxis"); + if (axisValue.isArray()) { + auto axisArray = axisValue.toArray(); + static const int AXIS_ARRAY_SIZE = 2; // axis can only have 2 children + if (axisArray.size() == AXIS_ARRAY_SIZE) { + Endpoint::Pointer first = parseEndpoint(axisArray.first()); + Endpoint::Pointer second = parseEndpoint(axisArray.last()); + if (first && second) { + return std::make_shared(first, second); + } + } + } + } + } + return Endpoint::Pointer(); +} + +Endpoint::Pointer UserInputMapper::parseAny(const QJsonValue& value) { if (value.isArray()) { Endpoint::List children; for (auto arrayItem : value.toArray()) { @@ -893,7 +922,19 @@ Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) { } return std::make_shared(children); } + return Endpoint::Pointer(); +} +Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) { + if (value.isObject()) { + auto axisEndpoint = parseAxis(value); + if (axisEndpoint) { + return axisEndpoint; + } + // if we have other types of endpoints that are objects, follow the axisEndpoint example, and place them here + } else if (value.isArray()) { + return parseAny(value); + } return parseEndpoint(value); } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index a32c3f3649..7684ecb7c5 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -159,6 +159,8 @@ namespace controller { RoutePointer parseRoute(const QJsonValue& value); EndpointPointer parseDestination(const QJsonValue& value); EndpointPointer parseSource(const QJsonValue& value); + EndpointPointer parseAxis(const QJsonValue& value); + EndpointPointer parseAny(const QJsonValue& value); EndpointPointer parseEndpoint(const QJsonValue& value); ConditionalPointer parseConditional(const QJsonValue& value); From 01d3805148a0655a050d1cfebc2037a85ed3f9cb Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 29 Oct 2015 18:44:43 -0700 Subject: [PATCH 213/232] ViveController - mapping of buttons --- interface/resources/controllers/vive.json | 27 ++++++++++--------- .../input-plugins/ViveControllerManager.cpp | 20 ++++++++------ 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index bcf4943ff8..6a3c562e44 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,23 +1,24 @@ { - "name": "XBox to Standard", + "name": "Vive to Standard", "channels": [ - { "from": "Vive.LY", "to": "Standard.LY" }, - { "from": "Vive.LX", "to": "Standard.LX" }, - { "from": "Vive.LT", "to": "Standard.LT" }, - { "from": "Vive.LB", "to": "Standard.LB" }, + { "from": "Vive.LY", "filters": [ "invert", { "type": "deadZone", "min": 0.7 } ], "to": "Standard.LY" }, + { "from": "Vive.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" }, + + { "from": "Vive.LT", "to": "Standard.LT" }, + { "from": "Vive.LB", "to": "Standard.LB" }, { "from": "Vive.LS", "to": "Standard.LS" }, - { "from": "Vive.RY", "to": "Standard.RY" }, + { "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" }, { "from": "Vive.RX", "to": "Standard.RX" }, - { "from": "Vive.RT", "to": "Standard.RT" }, - { "from": "Vive.RB", "to": "Standard.RB" }, + + { "from": "Vive.RT", "to": "Standard.RT" }, + { "from": "Vive.RB", "to": "Standard.RB" }, { "from": "Vive.RS", "to": "Standard.RS" }, - { "from": "Vive.Back", "to": "Standard.Back" }, - { "from": "Vive.Start", "to": "Standard.Start" }, - - { "from": "Vive.A", "to": "Standard.A" }, - { "from": "Vive.B", "to": "Standard.B" }, + { "from": "Vive.LeftPrimaryThumb", "to": "Standard.LeftPrimaryThumb" }, + { "from": "Vive.RightPrimaryThumb", "to": "Standard.RightPrimaryThumb" }, + { "from": "Vive.LeftSecondaryThumb", "to": "Standard.LeftSecondaryThumb" }, + { "from": "Vive.RightSecondaryThumb", "to": "Standard.RightSecondaryThumb" }, { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, { "from": "Vive.RightHand", "to": "Standard.RightHand" } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index c63d47b681..69303bb079 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -307,11 +307,13 @@ void ViveControllerManager::focusOutEvent() { // These functions do translation from the Steam IDs to the standard controller IDs void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { #ifdef Q_OS_WIN + axis += vr::k_EButton_Axis0; using namespace controller; if (axis == vr::k_EButton_SteamVR_Touchpad) { _axisStateMap[left ? LX : RX] = x; _axisStateMap[left ? LY : RY] = y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { + //FIX ME: Seems that enters here everytime _axisStateMap[left ? LT : RT] = x; } #endif @@ -325,15 +327,17 @@ void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, boo } if (button == vr::k_EButton_ApplicationMenu) { - // FIXME? - _buttonPressedMap.insert(left ? controller::B : controller::A); + _buttonPressedMap.insert(left ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); } else if (button == vr::k_EButton_Grip) { // Tony says these are harder to reach, so make them the meta buttons - _buttonPressedMap.insert(left ? controller::BACK : controller::START); - } else if (button == vr::k_EButton_SteamVR_Trigger) { _buttonPressedMap.insert(left ? controller::LB : controller::RB); + } else if (button == vr::k_EButton_SteamVR_Trigger) { + _buttonPressedMap.insert(left ? controller::LT : controller::RT); } else if (button == vr::k_EButton_SteamVR_Touchpad) { _buttonPressedMap.insert(left ? controller::LS : controller::RS); + } else if (button == vr::k_EButton_System) { + //FIX ME: not able to ovrewrite the behaviour of this button + _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB); } #endif } @@ -425,10 +429,10 @@ controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const makePair(LEFT_HAND, "LeftHand"), makePair(RIGHT_HAND, "RightHand"), - makePair(A, "A"), - makePair(B, "B"), - makePair(BACK, "Back"), - makePair(START, "Start"), + makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), + makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), + makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), + makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), }; //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); From ada32dd260528ab62efd9ce85eaa2062900040dc Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:17:52 -0700 Subject: [PATCH 214/232] typo --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 46dbe7576c..79df1c3bb8 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -780,9 +780,9 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM Q_ARG(AnimVariantResultHandler, resultHandler)); return; } - QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this, names, useNames); + QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames); QScriptValueList callingArguments; - callingArguments << javascriptParametgers; + callingArguments << javascriptParameters; QScriptValue result = callback.call(QScriptValue(), callingArguments); resultHandler(result); } From 5d1ba90f1ebd7f652485bf3f01b9b23a23ef2c37 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:18:34 -0700 Subject: [PATCH 215/232] More readable code. --- libraries/animation/src/AnimVariantMap.cpp | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index 2d9291db2d..c3e452fa1e 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -67,6 +67,7 @@ void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { _map[pair.first] = pair.second; } } + void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { if (QThread::currentThread() != source.engine()->thread()) { qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread(); @@ -84,10 +85,8 @@ void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { QScriptValue value = property.value(); if (value.isBool()) { set(property.name(), value.toBool()); - continue; } else if (value.isString()) { set(property.name(), value.toString()); - continue; } else if (value.isNumber()) { int asInteger = value.toInt32(); float asFloat = value.toNumber(); @@ -96,25 +95,27 @@ void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { } else { set(property.name(), asFloat); } - continue; - } else if (value.isObject()) { - QScriptValue x = value.property("x"); - if (x.isNumber()) { - QScriptValue y = value.property("y"); - if (y.isNumber()) { - QScriptValue z = value.property("z"); - if (z.isNumber()) { - QScriptValue w = value.property("w"); - if (w.isNumber()) { - set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber())); - } else { - set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber())); + } else { // Try to get x,y,z and possibly w + if (value.isObject()) { + QScriptValue x = value.property("x"); + if (x.isNumber()) { + QScriptValue y = value.property("y"); + if (y.isNumber()) { + QScriptValue z = value.property("z"); + if (z.isNumber()) { + QScriptValue w = value.property("w"); + if (w.isNumber()) { + set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber())); + } else { + set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber())); + } + continue; // we got either a vector or quaternion object, so don't fall through to warning } - continue; } } } + qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name(); + Q_ASSERT(false); } - qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name(); - } + } } From dcc173c93a5da52364666e78f85081a884055a4b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:21:24 -0700 Subject: [PATCH 216/232] comment. --- libraries/animation/src/Rig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b51907ea4a..ed5e07696d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -615,7 +615,7 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh int identifier = data.key(); StateHandler& value = data.value(); QScriptValue& function = value.function; - auto handleResult = [this, identifier](QScriptValue result) { + auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us. animationStateHandlerResult(identifier, result); }; // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture From f7d558a2528e036b6869f4fa528002444f7878c1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:26:54 -0700 Subject: [PATCH 217/232] comment --- libraries/animation/src/Rig.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ed5e07696d..4ddae07375 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -580,6 +580,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // Allow script to add/remove handlers and report results, from within their thread. QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread QMutexLocker locker(&_stateMutex); + // Find a safe id, even if there are lots of many scripts add and remove handlers repeatedly. while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap. _nextStateHandlerId++; } From 1918f1835cfc44ee995ff2d0f64274d7cae66f5e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:37:13 -0700 Subject: [PATCH 218/232] Tolerate AnimVars that are float when we want int, and vice versa. --- libraries/animation/src/AnimVariant.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index bd96dda77d..0d7c657058 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -61,8 +61,9 @@ public: void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } - int getInt() const { assert(_type == Type::Int); return _val.intVal; } - float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; } + int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; } + float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; } + const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast(&_val); } const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast(&_val); } const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast(&_val); } From d491ddc3d61a9fac53ac0efcb487eff6342967ad Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:43:57 -0700 Subject: [PATCH 219/232] comment. --- libraries/animation/src/AnimVariantMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariantMap.cpp index c3e452fa1e..8d320195dd 100644 --- a/libraries/animation/src/AnimVariantMap.cpp +++ b/libraries/animation/src/AnimVariantMap.cpp @@ -13,7 +13,7 @@ #include #include #include -#include "AnimVariant.h" +#include "AnimVariant.h" // which has AnimVariant/AnimVariantMap QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { if (QThread::currentThread() != engine->thread()) { From eb9e54de41eb7add77d5cc91cf85eb0164375106 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 19:45:23 -0700 Subject: [PATCH 220/232] Make AnimVariantXXX.xxx consistent. --- libraries/animation/src/{AnimVariantMap.cpp => AnimVariant.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libraries/animation/src/{AnimVariantMap.cpp => AnimVariant.cpp} (100%) diff --git a/libraries/animation/src/AnimVariantMap.cpp b/libraries/animation/src/AnimVariant.cpp similarity index 100% rename from libraries/animation/src/AnimVariantMap.cpp rename to libraries/animation/src/AnimVariant.cpp From 8a03383adc237f77105edb984d48a036b82584af Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 22:19:33 -0700 Subject: [PATCH 221/232] use makeAxis for some keyboard support --- .../resources/controllers/keyboardMouse.json | 46 ++++++------------- .../src/controllers/UserInputMapper.cpp | 6 ++- 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index fba55ffebd..e5abad761a 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -2,17 +2,24 @@ "name": "Keyboard/Mouse to Actions", "channels": [ - { "from": ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], - "when": [ "Application.InHMD", "Application.ComfortMode" ], + + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], "to": "Actions.StepYaw", "filters": [ + "constrainToInteger", { "type": "pulse", "interval": 0.5 }, - { "type": "scale", "scale": -15 } + { "type": "scale", "scale": 15 } ] }, - { "from": ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"], + { "from": { "makeAxis" : [ + ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], + ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ] + }, + "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw", "filters": @@ -22,25 +29,10 @@ ] }, - { "from": "Keyboard.MouseMoveLeft", - "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "pulse", "interval": 0.5 }, - { "type": "scale", "scale": -15 } - ] - }, - - { "from": "Keyboard.MouseMoveRight", - "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], - "to": "Actions.StepYaw", - "filters": - [ - { "type": "pulse", "interval": 0.5 }, - { "type": "scale", "scale": 15 } - ] - }, + { "from": ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], "to": "Actions.YAW_LEFT" }, + { "from": ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"], "to": "Actions.YAW_RIGHT" }, + { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, @@ -53,8 +45,6 @@ { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, @@ -67,21 +57,15 @@ { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, - { "from": "Keyboard.Left", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.Right", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.TouchpadLeft", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.TouchpadRight", "to": "Actions.YAW_RIGHT" }, { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index d7f57d91e3..d33e215797 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -489,7 +489,11 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { // If the source hasn't been written yet, defer processing of this route auto source = route->source; - if (!force && source->writeable()) { + auto sourceInput = source->getInput(); + if (sourceInput.device == STANDARD_DEVICE && !force && source->writeable()) { + if (debugRoutes && route->debug) { + qCDebug(controllers) << "Source not yet written, deferring"; + } return false; } From 70f31563284c06f7f58c2ce8f2f2794cc392f1bd Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 22:37:12 -0700 Subject: [PATCH 222/232] make sure CompositeEndpoints properly report readability more axis based keyboard inputs --- .../resources/controllers/keyboardMouse.json | 17 ++++++++++++----- .../impl/endpoints/CompositeEndpoint.cpp | 4 ++++ .../impl/endpoints/CompositeEndpoint.h | 2 ++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index e5abad761a..096cdb35d3 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -19,7 +19,6 @@ ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] ] }, - "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw", "filters": @@ -29,10 +28,18 @@ ] }, - { "from": ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], "to": "Actions.YAW_LEFT" }, - { "from": ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"], "to": "Actions.YAW_RIGHT" }, - { "from": "Keyboard.MouseMoveLeft", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.MouseMoveRight", "when": "Keyboard.RightMouseClick", "to": "Actions.YAW_RIGHT" }, + { "from": { "makeAxis" : [ + ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], + ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ] + }, + "to": "Actions.Yaw" + }, + + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + "when": "Keyboard.RightMouseClick", + "to": "Actions.Yaw", + }, { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp index e5088ef72c..913bf0136b 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp @@ -20,6 +20,10 @@ CompositeEndpoint::CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer } } +bool CompositeEndpoint::readable() const { + return first->readable() && second->readable(); +} + float CompositeEndpoint::value() { float result = first->value() * -1.0f + second->value(); return result; diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h index ab8b97aa50..c6ec90b7c8 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h @@ -19,6 +19,8 @@ namespace controller { virtual float value() override; virtual void apply(float newValue, const Pointer& source) override; + virtual bool readable() const override; + }; } From 9607812116aac4a02a0efe50a7ac72ec2aa9243d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 30 Oct 2015 10:18:51 -0700 Subject: [PATCH 223/232] Fixing input plugin dependencies --- libraries/input-plugins/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index cd3fae8e6a..0e21d4a40c 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME input-plugins) setup_hifi_library() -link_hifi_libraries(shared plugins controllers render-utils) +link_hifi_libraries(shared plugins controllers script-engine render-utils) GroupSources("src/input-plugins") From f887bbd45c1a954a3f34cb29d430407541c507c3 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 30 Oct 2015 11:06:20 -0700 Subject: [PATCH 224/232] Fixed controllerExample parse error --- examples/example/avatarcontrol/controllerExample.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example/avatarcontrol/controllerExample.js b/examples/example/avatarcontrol/controllerExample.js index fe23ce0e8e..8d7996b02b 100644 --- a/examples/example/avatarcontrol/controllerExample.js +++ b/examples/example/avatarcontrol/controllerExample.js @@ -22,7 +22,7 @@ triggers[1] = Controller.Standard.RT; function checkController(deltaTime) { var triggerToggled = false; for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) { - var triggerValue = Controller.getValue(triggers[t]]); + var triggerValue = Controller.getValue(triggers[t]); if (triggerPulled[t]) { // must release to at least 0.1 if (triggerValue < 0.1) { From 393f6a4c763df19b220fe077179c2d1f66f5e956 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 30 Oct 2015 11:35:50 -0700 Subject: [PATCH 225/232] Fixing a bad field ID for keyLight.AmbientURL and bumping the domain version --- libraries/entities/src/EntityPropertyFlags.h | 2 +- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 51a0c34c76..65060c8d45 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -189,7 +189,7 @@ enum EntityPropertyList { PROP_BACKGROUND_MODE = PROP_MODEL_URL, PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, PROP_SKYBOX_URL = PROP_ANIMATION_FPS, - PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_FRAME_INDEX, + PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING, // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for // other properties which will never overlap with each other. diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 7062942c51..a46a9693ac 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP; + return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 64e0a9d8e4..553c12f8e3 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -144,5 +144,6 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER = 44; const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45; const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; +const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; #endif // hifi_PacketHeaders_h From ce7fed2292cc4b81f975c4022cf7d57a3755367c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 13:16:31 -0700 Subject: [PATCH 226/232] fix json --- interface/resources/controllers/keyboardMouse.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 096cdb35d3..fbc1533fff 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -38,7 +38,7 @@ { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, "when": "Keyboard.RightMouseClick", - "to": "Actions.Yaw", + "to": "Actions.Yaw" }, { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, From 22324eb281f7b3f46f91fdbe06c04f0196976f13 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 14:55:33 -0700 Subject: [PATCH 227/232] more tweaking of keyboard input routes --- .../resources/controllers/keyboardMouse.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index fbc1533fff..8af6b1dc98 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -2,6 +2,15 @@ "name": "Keyboard/Mouse to Actions", "channels": [ + { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, + { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, + { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, + { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, + { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], @@ -41,15 +50,6 @@ "to": "Actions.Yaw" }, - { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, - { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, - { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, - { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, - { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, - { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, From b8a2fcbb2c74dfac021108e244c423e83e888453 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 30 Oct 2015 15:21:48 -0700 Subject: [PATCH 228/232] Vive controller - fix inverted inputs --- .../input-plugins/src/input-plugins/ViveControllerManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 69303bb079..e90006e014 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -254,7 +254,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } numTrackedControllers++; - bool left = numTrackedControllers == 1; + bool left = numTrackedControllers == 2; const mat4& mat = _trackedDevicePoseMat4[device]; @@ -307,13 +307,13 @@ void ViveControllerManager::focusOutEvent() { // These functions do translation from the Steam IDs to the standard controller IDs void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { #ifdef Q_OS_WIN + //FIX ME? It enters here every frame: probably we want to enter only if an event occurs axis += vr::k_EButton_Axis0; using namespace controller; if (axis == vr::k_EButton_SteamVR_Touchpad) { _axisStateMap[left ? LX : RX] = x; _axisStateMap[left ? LY : RY] = y; } else if (axis == vr::k_EButton_SteamVR_Trigger) { - //FIX ME: Seems that enters here everytime _axisStateMap[left ? LT : RT] = x; } #endif From 085282db4f4cf1ed69ec4489c3906a3ab9f32bcf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 30 Oct 2015 16:13:04 -0700 Subject: [PATCH 229/232] Crash fix for AvatarManager when iterating over _avatarHash The main problem is that a null shared pointer was inserted into the _avatarHash via the AvatarManager::getAvatarBySessionID(). When the sessionID is not present in the _avatarHash, [QHash](http://doc.qt.io/qt-5/qhash.html#operator-5b-5d) will *insert* an empty smart_ptr into the hash. --- interface/src/avatar/AvatarManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index fbfbbad2de..b0da8faeca 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -366,5 +366,10 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) return std::static_pointer_cast(_myAvatar); } QReadLocker locker(&_hashLock); - return _avatarHash[sessionID]; + auto iter = _avatarHash.find(sessionID); + if (iter != _avatarHash.end()) { + return iter.value(); + } else { + return AvatarSharedPointer(); + } } From 77e0023b43ddcfe6db2fd7b9852cba2b0977cdd2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Oct 2015 16:31:15 -0700 Subject: [PATCH 230/232] Fix lifetime bug with first PolyLine in InfiniteLine --- examples/libraries/line.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/line.js b/examples/libraries/line.js index d31a34867b..c21bf2f3ad 100644 --- a/examples/libraries/line.js +++ b/examples/libraries/line.js @@ -102,7 +102,7 @@ InfiniteLine = function(position, color, lifetime) { this.position = position; this.color = color; this.lifetime = lifetime === undefined ? DEFAULT_LIFETIME : lifetime; - this.lines = [new PolyLine(position, color, 0.01)]; + this.lines = []; this.size = 0; }; From f50e1a0977f92d28c5e117f80a2e7723e6acd8ae Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 30 Oct 2015 17:18:37 -0700 Subject: [PATCH 231/232] Fix for rare crash in LogHandler::flushRepeatedMessages() This can happen when LogHandler::flushRepetedMessages is called on the main thread, while the application is printing messages on a separate thread. The access to the _lastRepeatedMessage QHash was not guarded. I've added two mutexes to guard access to both the repeatedMessage hashes/regexes and the onlyOnceMessages/regexes. This will unfortunately incur a performance hit for frequent debug logging, but that's better then crashing. Also, I've added the ability to print threadIDs as well as Process ids. This is helpful when debugging multi-threaded access to shared variables. --- assignment-client/src/AssignmentClient.cpp | 2 +- .../src/AssignmentClientMonitor.cpp | 2 +- libraries/shared/src/LogHandler.cpp | 66 ++++++++++++------- libraries/shared/src/LogHandler.h | 26 ++++---- 4 files changed, 60 insertions(+), 36 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index f4f98114d0..bf5f9c3b7f 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -74,7 +74,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); // make sure we output process IDs for a child AC otherwise it's insane to parse - LogHandler::getInstance().setShouldOutputPID(true); + LogHandler::getInstance().setShouldOutputProcessID(true); // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 58cf3c49f3..7b3d5695e1 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -44,7 +44,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME); // make sure we output process IDs for a monitor otherwise it's insane to parse - LogHandler::getInstance().setShouldOutputPID(true); + LogHandler::getInstance().setShouldOutputProcessID(true); // create a NodeList so we can receive stats from children DependencyManager::registerInheritance(); diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index cc3519e43e..0e05df277b 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -12,9 +12,10 @@ #include -#include -#include -#include +#include +#include +#include +#include #include "LogHandler.h" @@ -24,13 +25,14 @@ LogHandler& LogHandler::getInstance() { } LogHandler::LogHandler() : - _shouldOutputPID(false) + _shouldOutputProcessID(false), + _shouldOutputThreadID(false) { // setup our timer to flush the verbose logs every 5 seconds QTimer* logFlushTimer = new QTimer(this); connect(logFlushTimer, &QTimer::timeout, this, &LogHandler::flushRepeatedMessages); logFlushTimer->start(VERBOSE_LOG_INTERVAL_SECONDS * 1000); - + // when the log handler is first setup we should print our timezone QString timezoneString = "Time zone: " + QDateTime::currentDateTime().toString("t"); printf("%s\n", qPrintable(timezoneString)); @@ -57,51 +59,55 @@ const char* stringForLogType(LogMsgType msgType) { const QString DATE_STRING_FORMAT = "MM/dd hh:mm:ss"; void LogHandler::flushRepeatedMessages() { + QMutexLocker locker(&_repeatedMessageLock); QHash::iterator message = _repeatMessageCountHash.begin(); while (message != _repeatMessageCountHash.end()) { - + if (message.value() > 0) { QString repeatMessage = QString("%1 repeated log entries matching \"%2\" - Last entry: \"%3\"") .arg(message.value()).arg(message.key()).arg(_lastRepeatedMessage.value(message.key())); - + QMessageLogContext emptyContext; printMessage(LogSuppressed, emptyContext, repeatMessage); } - + _lastRepeatedMessage.remove(message.key()); message = _repeatMessageCountHash.erase(message); } } QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& context, const QString& message) { - + if (message.isEmpty()) { return QString(); } - + if (type == LogDebug) { // for debug messages, check if this matches any of our regexes for repeated log messages + QMutexLocker locker(&_repeatedMessageLock); foreach(const QString& regexString, getInstance()._repeatedMessageRegexes) { QRegExp repeatRegex(regexString); if (repeatRegex.indexIn(message) != -1) { - + if (!_repeatMessageCountHash.contains(regexString)) { // we have a match but didn't have this yet - output the first one _repeatMessageCountHash[regexString] = 0; - + // break the foreach so we output the first match break; } else { // we have a match - add 1 to the count of repeats for this message and set this as the last repeated message _repeatMessageCountHash[regexString] += 1; _lastRepeatedMessage[regexString] = message; - + // return out, we're not printing this one return QString(); } } } - + } + if (type == LogDebug) { + QMutexLocker locker(&_onlyOnceMessageLock); // see if this message is one we should only print once foreach(const QString& regexString, getInstance()._onlyOnceMessageRegexes) { QRegExp onlyOnceRegex(regexString); @@ -118,23 +124,27 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont } } } - + // log prefix is in the following format - // [TIMESTAMP] [DEBUG] [PID] [TARGET] logged string - + // [TIMESTAMP] [DEBUG] [PID] [TID] [TARGET] logged string + QString prefixString = QString("[%1]").arg(QDateTime::currentDateTime().toString(DATE_STRING_FORMAT)); - + prefixString.append(QString(" [%1]").arg(stringForLogType(type))); - - if (_shouldOutputPID) { + + if (_shouldOutputProcessID) { prefixString.append(QString(" [%1]").arg(QCoreApplication::instance()->applicationPid())); - } - + + if (_shouldOutputThreadID) { + size_t threadID = (size_t)QThread::currentThreadId(); + prefixString.append(QString(" [%1]").arg(threadID)); + } + if (!_targetName.isEmpty()) { prefixString.append(QString(" [%1]").arg(_targetName)); } - + QString logMessage = QString("%1 %2").arg(prefixString, message.split("\n").join("\n" + prefixString + " ")); fprintf(stdout, "%s\n", qPrintable(logMessage)); return logMessage; @@ -143,3 +153,13 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont void LogHandler::verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { getInstance().printMessage((LogMsgType) type, context, message); } + +const QString& LogHandler::addRepeatedMessageRegex(const QString& regexString) { + QMutexLocker locker(&_repeatedMessageLock); + return *_repeatedMessageRegexes.insert(regexString); +} + +const QString& LogHandler::addOnlyOnceMessageRegex(const QString& regexString) { + QMutexLocker locker(&_onlyOnceMessageLock); + return *_onlyOnceMessageRegexes.insert(regexString); +} diff --git a/libraries/shared/src/LogHandler.h b/libraries/shared/src/LogHandler.h index 6af721f96c..fff6ca43d6 100644 --- a/libraries/shared/src/LogHandler.h +++ b/libraries/shared/src/LogHandler.h @@ -34,34 +34,38 @@ class LogHandler : public QObject { Q_OBJECT public: static LogHandler& getInstance(); - + /// sets the target name to output via the verboseMessageHandler, called once before logging begins /// \param targetName the desired target name to output in logs void setTargetName(const QString& targetName) { _targetName = targetName; } - - void setShouldOutputPID(bool shouldOutputPID) { _shouldOutputPID = shouldOutputPID; } - + + void setShouldOutputProcessID(bool shouldOutputProcessID) { _shouldOutputProcessID = shouldOutputProcessID; } + void setShouldOutputThreadID(bool shouldOutputThreadID) { _shouldOutputThreadID = shouldOutputThreadID; } + QString printMessage(LogMsgType type, const QMessageLogContext& context, const QString &message); - + /// a qtMessageHandler that can be hooked up to a target that links to Qt /// prints various process, message type, and time information static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message); - - const QString& addRepeatedMessageRegex(const QString& regexString) { return *_repeatedMessageRegexes.insert(regexString); } - const QString& addOnlyOnceMessageRegex(const QString& regexString) { return *_onlyOnceMessageRegexes.insert(regexString); } + + const QString& addRepeatedMessageRegex(const QString& regexString); + const QString& addOnlyOnceMessageRegex(const QString& regexString); private: LogHandler(); - + void flushRepeatedMessages(); - + QString _targetName; - bool _shouldOutputPID; + bool _shouldOutputProcessID; + bool _shouldOutputThreadID; QSet _repeatedMessageRegexes; QHash _repeatMessageCountHash; QHash _lastRepeatedMessage; + QMutex _repeatedMessageLock; QSet _onlyOnceMessageRegexes; QHash _onlyOnceMessageCountHash; + QMutex _onlyOnceMessageLock; }; #endif // hifi_LogHandler_h From 2eb62f2fd89fb9c49107a72bdab9106dc00a8efa Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 30 Oct 2015 17:44:06 -0700 Subject: [PATCH 232/232] LogHandler: fix for linux build --- libraries/shared/src/LogHandler.cpp | 2 ++ libraries/shared/src/LogHandler.h | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index 0e05df277b..7dd1aceb3d 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "LogHandler.h" diff --git a/libraries/shared/src/LogHandler.h b/libraries/shared/src/LogHandler.h index fff6ca43d6..a74a6287d7 100644 --- a/libraries/shared/src/LogHandler.h +++ b/libraries/shared/src/LogHandler.h @@ -13,11 +13,11 @@ #ifndef hifi_LogHandler_h #define hifi_LogHandler_h -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include const int VERBOSE_LOG_INTERVAL_SECONDS = 5;