From 7987595dcd2bf66abcf843d2c4ff0a2efa892b18 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 8 Oct 2015 11:32:10 -0700 Subject: [PATCH 0001/1003] set the bearer poll timeout for wlan scan in Qt 5.5 --- assignment-client/src/AssignmentClientApp.cpp | 4 ++++ domain-server/src/DomainServer.cpp | 4 ++++ interface/src/Application.cpp | 6 +++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 3b9f8af868..cddce5de7d 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -27,6 +27,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv) { + // 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); + # ifndef WIN32 setvbuf(stdout, NULL, _IOLBF, 0); # endif diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 48bb9bed62..b5fd9f2b20 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -62,6 +62,10 @@ DomainServer::DomainServer(int argc, char* argv[]) : LogUtils::init(); Setting::init(); + + // 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); connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c427379f74..e6b158d37e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -364,7 +364,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : thread()->setObjectName("Main Thread"); setInstance(this); - + + // 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(); From 4e72d945b634605ef37d7bb13dc282bef72aeb80 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 9 Oct 2015 14:00:35 -0700 Subject: [PATCH 0002/1003] 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 0003/1003] 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 0004/1003] 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 0005/1003] 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 0006/1003] 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 0007/1003] 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 0008/1003] 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 0009/1003] 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 0010/1003] 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 0011/1003] 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 0012/1003] 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 0013/1003] 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 0014/1003] 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 7d3b300037548f26ff8e55cf82d1622fa7f68124 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sun, 11 Oct 2015 14:51:12 +0200 Subject: [PATCH 0015/1003] Fix set avatar rotations from scripts (different tread) --- interface/src/avatar/MyAvatar.cpp | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..de52485407 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1154,32 +1154,41 @@ void MyAvatar::setJointTranslations(QVector jointTranslations) { } void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation), + Q_ARG(const glm::vec3&, translation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, true, rotation, translation, SCRIPT_PRIORITY); } void MyAvatar::setJointRotation(int index, const glm::quat& rotation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointRotation(index, true, rotation, SCRIPT_PRIORITY); } void MyAvatar::setJointTranslation(int index, const glm::vec3& translation) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority - _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation)); + return; } + // HACK: ATM only JS scripts call setJointData() on MyAvatar so we hardcode the priority + _rig->setJointTranslation(index, true, translation, SCRIPT_PRIORITY); } void MyAvatar::clearJointData(int index) { - if (QThread::currentThread() == thread()) { - // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority - _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); - _rig->clearJointAnimationPriority(index); + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index)); + return; } + // HACK: ATM only JS scripts call clearJointData() on MyAvatar so we hardcode the priority + _rig->setJointState(index, false, glm::quat(), glm::vec3(), 0.0f); + _rig->clearJointAnimationPriority(index); } void MyAvatar::clearJointsData() { From a6da91ee35e5a76a9c89089207e6f3016173e1c5 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 12 Oct 2015 19:03:34 +0200 Subject: [PATCH 0016/1003] Fix walk.js script --- examples/libraries/walkApi.js | 10 +++++----- examples/walk.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 8b99ad2a98..5b42a8fb78 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -82,7 +82,7 @@ Avatar = function() { // only need to zero right leg IK chain and hips if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointData(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(joint), Quat.fromPitchYawRollDegrees(0, 0, 0)); } } this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; @@ -112,16 +112,16 @@ Avatar = function() { this.poseFingers = function() { for (knuckle in walkAssets.animationReference.leftHand) { if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, -4)); } else { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, 5)); } } for (knuckle in walkAssets.animationReference.rightHand) { if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, 4)); } else { - MyAvatar.setJointData(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, -5)); } } }; diff --git a/examples/walk.js b/examples/walk.js index 8d54fecc92..02bc23a2e2 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -449,6 +449,6 @@ function renderMotion() { } // apply rotations - MyAvatar.setJointData(jointName, Quat.fromVec3Degrees(jointRotations)); + MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(jointName), Quat.fromVec3Degrees(jointRotations)); } } \ No newline at end of file From fb4aad1f3224018dcc8198e2eee6a908f6af73e1 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 10:11:46 -0700 Subject: [PATCH 0017/1003] adding whiteboard --- examples/{ => painting}/closePaint.js | 0 examples/{ => painting}/paint.js | 0 .../painting/whiteboard/whiteboardSpawner.js | 32 +++++++++++++++++++ 3 files changed, 32 insertions(+) rename examples/{ => painting}/closePaint.js (100%) rename examples/{ => painting}/paint.js (100%) create mode 100644 examples/painting/whiteboard/whiteboardSpawner.js diff --git a/examples/closePaint.js b/examples/painting/closePaint.js similarity index 100% rename from examples/closePaint.js rename to examples/painting/closePaint.js diff --git a/examples/paint.js b/examples/painting/paint.js similarity index 100% rename from examples/paint.js rename to examples/painting/paint.js diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js new file mode 100644 index 0000000000..bf6253c0ac --- /dev/null +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -0,0 +1,32 @@ +// +// whiteBoard.js +// examples/painting +// +// Created by Eric Levina on 10/12/15. +// Copyright 2015 High Fidelity, Inc. +// +// Run this script to spawn a whiteboard that one can paint on +// +// 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/utils.js"); + +var rotation = Quat.safeEulerAngles(Camera.getOrientation()); +rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0) +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); +var whiteboard = Entities.addEntity({ + type: "Box", + position: center, + rotation: rotation, + dimensions: {x: 2, y: 1.5, z: 0.01}, + color: {red: 255, green: 255, blue: 255} +}); + +function cleanup() { + Entities.deleteEntity(whiteboard); +} + + +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 56a2aa101cc6d45f93cfeed60134a1598c6c0578 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 10:44:18 -0700 Subject: [PATCH 0018/1003] baseline judder test for whiteboard painting- pre entity ray test --- .../whiteboard/whiteboardEntityScript.js | 48 +++++++++++++++++++ .../painting/whiteboard/whiteboardSpawner.js | 10 +++- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 examples/painting/whiteboard/whiteboardEntityScript.js diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js new file mode 100644 index 0000000000..da2e06d397 --- /dev/null +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -0,0 +1,48 @@ +// +// whiteBoardEntityScript.js +// examples/painting/whiteboard +// +// Created by Eric Levin on 10/12/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 + +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + +/*global Whiteboard */ + +(function() { + + var _this; + Whiteboard = function() { + _this = this; + }; + + Whiteboard.prototype = { + + setRightHand: function() { + this.hand = 'RIGHT'; + }, + + setLeftHand: function() { + this.hand = 'LEFT'; + }, + + startFarGrabNonColliding: function() { + this.whichHand = this.hand; + }, + + continueFarGrabbingNonColliding: function() {}, + + preload: function(entityID) { + this.entityID = entityID; + this.position = Entities.getEntityProperties(this.entityID, "position").position; + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new Whiteboard(); +}); \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index bf6253c0ac..31a72212c3 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -1,5 +1,5 @@ // -// whiteBoard.js +// whiteBoardSpawner.js // examples/painting // // Created by Eric Levina on 10/12/15. @@ -10,16 +10,22 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + + Script.include("../../libraries/utils.js"); +var scriptURL = Script.resolvePath("whiteBoardEntityScript.js"); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); -rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0) +rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); +center.y += 0.4; var whiteboard = Entities.addEntity({ type: "Box", position: center, rotation: rotation, + script: scriptURL, dimensions: {x: 2, y: 1.5, z: 0.01}, color: {red: 255, green: 255, blue: 255} }); From 14f511350d5c3b88408cd48f710188ab2842a8a7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 10 Oct 2015 22:34:11 -0700 Subject: [PATCH 0019/1003] 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 96fd63413878c5fb75f752fbb4450708bccd91cd Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 11:02:47 -0700 Subject: [PATCH 0020/1003] Pre- ray cast --- .../painting/whiteboard/whiteboardEntityScript.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index da2e06d397..c8e914b0e0 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -16,6 +16,8 @@ (function() { var _this; + var RIGHT_HAND = 1; + var LEFT_HAND = 0; Whiteboard = function() { _this = this; }; @@ -23,18 +25,20 @@ Whiteboard.prototype = { setRightHand: function() { - this.hand = 'RIGHT'; + this.hand = RIGHT_HAND; }, setLeftHand: function() { - this.hand = 'LEFT'; + this.hand = LEFT_HAND; }, startFarGrabNonColliding: function() { - this.whichHand = this.hand; + this.activeHand = this.hand; }, - continueFarGrabbingNonColliding: function() {}, + continueFarGrabbingNonColliding: function() { + var handClick = Controller.findAction(handClickString); + }, preload: function(entityID) { this.entityID = entityID; From 8dddf0e17a3de794c8a36d94fbb0ceb69d189196 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 11:15:04 -0700 Subject: [PATCH 0021/1003] Added findRayIntersection, and confirmed judder grew a lot worse --- .../whiteboard/whiteboardEntityScript.js | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index c8e914b0e0..f9f272d5a3 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -18,6 +18,8 @@ var _this; var RIGHT_HAND = 1; var LEFT_HAND = 0; + var SPATIAL_CONTROLLERS_PER_PALM = 2; + var TIP_CONTROLLER_OFFSET = 1; Whiteboard = function() { _this = this; }; @@ -33,11 +35,33 @@ }, startFarGrabNonColliding: function() { - this.activeHand = this.hand; + if (this.hand === RIGHT_HAND) { + this.getHandPosition = MyAvatar.getRightPalmPosition; + this.getHandRotation = MyAvatar.getRightPalmRotation; + this.triggerAction = Controller.findAction("RIGHT_HAND_CLICK"); + } else if (this.hand === LEFT_HAND) { + this.getHandPosition = MyAvatar.getLeftPalmPosition; + this.getHandRotation = MyAvatar.getLeftPalmRotation; + this.triggerAction = Controller.findAction("LEFT_HAND_CLICK"); + } }, continueFarGrabbingNonColliding: function() { - var handClick = Controller.findAction(handClickString); + var handPosition = this.getHandPosition(); + var pickRay = { + origin: handPosition, + direction: Quat.getUp(this.getHandRotation()) + }; + this.intersection = Entities.findRayIntersection(pickRay, true); + if (this.intersection.intersects) { + if(JSON.stringify(this.intersection.entityID) === JSON.stringify(this.entityID)) { + print('YAAAAA') + } + } + }, + + releaseGrab: function() { + }, preload: function(entityID) { From ebeb87ba62d7c6b0b82e3fc6bfd69ccc42e38553 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Oct 2015 11:21:40 -0700 Subject: [PATCH 0022/1003] 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 c666c9fb9b5df854990826a27d2b665aeb86a51e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 Oct 2015 13:40:33 -0700 Subject: [PATCH 0023/1003] Fix warning --- .../input-plugins/src/input-plugins/StandardController.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/StandardController.cpp b/libraries/input-plugins/src/input-plugins/StandardController.cpp index 4fb4a23654..9b89c081b3 100644 --- a/libraries/input-plugins/src/input-plugins/StandardController.cpp +++ b/libraries/input-plugins/src/input-plugins/StandardController.cpp @@ -17,8 +17,6 @@ const float CONTROLLER_THRESHOLD = 0.3f; -const float MAX_AXIS = 32768.0f; - StandardController::~StandardController() { } From d01dda9c8185136bd07c7dd6531ca7c90c470b72 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 14:40:11 -0700 Subject: [PATCH 0024/1003] Adding in functionality to include only specified Ids in ray picking --- examples/controllers/handControllerGrab.js | 2 +- libraries/entities/src/EntityScriptingInterface.cpp | 10 +++++----- libraries/entities/src/EntityScriptingInterface.h | 6 +++--- libraries/entities/src/EntityTreeElement.cpp | 4 +++- libraries/entities/src/EntityTreeElement.h | 2 +- libraries/octree/src/Octree.cpp | 7 ++++--- libraries/octree/src/Octree.h | 1 + libraries/octree/src/OctreeElement.cpp | 6 +++--- libraries/octree/src/OctreeElement.h | 4 ++-- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 05dfc8e5d5..26cb522f9a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -67,7 +67,7 @@ var MSEC_PER_SEC = 1000.0; var startTime = Date.now(); var LIFETIME = 10; var ACTION_LIFETIME = 10; // seconds -var PICKS_PER_SECOND_PER_HAND = 5; +var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; // states for the state machine diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 0dd0129a1e..47fee0ae4e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -279,16 +279,16 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking) { - return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, const QVector& entityIdsToIgnore, bool precisionPicking) { + return findRayIntersectionWorker(ray, Octree::TryLock, entityIdsToIgnore, precisionPicking); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking) { - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, const QVector& entityIdsToIgnore, bool precisionPicking) { + return findRayIntersectionWorker(ray, Octree::Lock, entityIdsToIgnore, precisionPicking); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, - Octree::lockType lockType, + Octree::lockType lockType, const QVector& entityIdsToIgnore, bool precisionPicking) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index cf10bdb997..632019a575 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -111,11 +111,11 @@ public slots: /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, const QVector& entityIdsToInclude, bool precisionPicking = false); /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, const QVector& entityIdsToInclude, bool precisionPicking = false); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; @@ -185,7 +185,7 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode - RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, + RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, const QVector& entityIdsToInclude, bool precisionPicking); EntityTreePointer _entityTree; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 48c30caf20..d1f7adef94 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -495,12 +495,14 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking, float distanceToElementCube) { + const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... int entityNumber = 0; bool somethingIntersected = false; forEachEntity([&](EntityItemPointer entity) { + + qDebug() << "entity Ids to ignore:************* " << entityIdsToInclude; AABox entityBox = entity->getAABox(); float localDistance; BoxFace localFace; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index c26b5417ed..0a47542e65 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -144,7 +144,7 @@ public: virtual bool canRayIntersect() const { return hasEntities(); } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 758a47bc61..3b6467401c 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -703,6 +703,7 @@ public: float& distance; BoxFace& face; glm::vec3& surfaceNormal; + const QVector& entityIdsToInclude; void** intersectedObject; bool found; bool precisionPicking; @@ -712,7 +713,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; if (element->findRayIntersection(args->origin, args->direction, keepSearching, - args->element, args->distance, args->face, args->surfaceNormal, + args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->intersectedObject, args->precisionPicking)) { args->found = true; } @@ -721,9 +722,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, intersectedObject, false, precisionPicking}; + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking}; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index dececf1456..3ab1ad8ec4 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -300,6 +300,7 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, void** intersectedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL, diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 25758c29d9..9a48079338 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -575,7 +575,7 @@ void OctreeElement::notifyUpdateHooks() { bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking) { keepSearching = true; // assume that we will continue searching after this. @@ -601,7 +601,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3 if (_cube.contains(origin) || distanceToElementCube < distance) { if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails, - face, localSurfaceNormal, intersectedObject, precisionPicking, distanceToElementCube)) { + face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) { if (distanceToElementDetails < distance) { distance = distanceToElementDetails; @@ -616,7 +616,7 @@ bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3 bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { // we did hit this element, so calculate appropriate distances diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index fcdc9985e3..c132d437cd 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -120,12 +120,12 @@ public: virtual bool canRayIntersect() const { return isLeaf(); } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube); /// \param center center of sphere in meters From 84f0c404adf6d3b5f628cf2645fd90eb40dca687 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 14:55:52 -0700 Subject: [PATCH 0025/1003] fix bug related to camera lock for entity orbit and pan --- examples/edit.js | 593 +++++++++++++++++++------ examples/libraries/entityCameraTool.js | 208 +++++++-- 2 files changed, 622 insertions(+), 179 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 0afc3ec1b6..09d30c712b 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -39,7 +39,9 @@ var lightOverlayManager = new LightOverlayManager(); var cameraManager = new CameraManager(); var grid = Grid(); -gridTool = GridTool({ horizontalGrid: grid }); +gridTool = GridTool({ + horizontalGrid: grid +}); gridTool.setVisible(false); var entityListTool = EntityListTool(); @@ -92,8 +94,8 @@ var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissi var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain." var modelURLs = [ - "Insert the URL to your FBX" - ]; + "Insert the URL to your FBX" +]; var mode = 0; var isActive = false; @@ -109,20 +111,30 @@ var importingSVOImageOverlay = Overlays.addOverlay("image", { width: 20, height: 20, alpha: 1.0, - color: { red: 255, green: 255, blue: 255 }, + color: { + red: 255, + green: 255, + blue: 255 + }, x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH, y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT, visible: false, }); var importingSVOTextOverlay = Overlays.addOverlay("text", { - font: { size: 14 }, + font: { + size: 14 + }, text: "Importing SVO...", leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN, x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN, y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN, width: IMPORTING_SVO_OVERLAY_WIDTH, height: IMPORTING_SVO_OVERLAY_HEIGHT, - backgroundColor: { red: 80, green: 80, blue: 80 }, + backgroundColor: { + red: 80, + green: 80, + blue: 80 + }, backgroundAlpha: 0.7, visible: false, }); @@ -131,7 +143,7 @@ var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); marketplaceWindow.setVisible(false); -var toolBar = (function () { +var toolBar = (function() { var that = {}, toolBar, activeButton, @@ -146,7 +158,7 @@ var toolBar = (function () { browseMarketplaceButton; function initialize() { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function (windowDimensions, toolbar) { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) { return { x: windowDimensions.x - 8 - toolbar.width, y: (windowDimensions.y - toolbar.height) / 2 @@ -163,7 +175,12 @@ var toolBar = (function () { activeButton = toolBar.addTool({ imageURL: toolIconUrl + "edit-status.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -172,7 +189,12 @@ var toolBar = (function () { newModelButton = toolBar.addTool({ imageURL: toolIconUrl + "upload.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -181,7 +203,12 @@ var toolBar = (function () { newCubeButton = toolBar.addTool({ imageURL: toolIconUrl + "add-cube.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -190,7 +217,12 @@ var toolBar = (function () { newSphereButton = toolBar.addTool({ imageURL: toolIconUrl + "add-sphere.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -199,7 +231,12 @@ var toolBar = (function () { newLightButton = toolBar.addTool({ imageURL: toolIconUrl + "light.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -208,7 +245,12 @@ var toolBar = (function () { newTextButton = toolBar.addTool({ imageURL: toolIconUrl + "add-text.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + subImage: { + x: 0, + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -217,7 +259,12 @@ var toolBar = (function () { newWebButton = toolBar.addTool({ imageURL: "https://hifi-public.s3.amazonaws.com/images/www.svg", - subImage: { x: 0, y: 0, width: 128, height: 128 }, + subImage: { + x: 0, + y: 0, + width: 128, + height: 128 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -226,7 +273,12 @@ var toolBar = (function () { newZoneButton = toolBar.addTool({ imageURL: toolIconUrl + "zonecube_text.svg", - subImage: { x: 0, y: 128, width: 128, height: 128 }, + subImage: { + x: 0, + y: 128, + width: 128, + height: 128 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -235,7 +287,12 @@ var toolBar = (function () { newPolyVoxButton = toolBar.addTool({ imageURL: toolIconUrl + "polyvox.svg", - subImage: { x: 0, y: 0, width: 256, height: 256 }, + subImage: { + x: 0, + y: 0, + width: 256, + height: 256 + }, width: toolWidth, height: toolHeight, alpha: 0.9, @@ -293,6 +350,7 @@ var toolBar = (function () { var RESIZE_INTERVAL = 50; var RESIZE_TIMEOUT = 120000; // 2 minutes var RESIZE_MAX_CHECKS = RESIZE_TIMEOUT / RESIZE_INTERVAL; + function addModel(url) { var entityID = createNewEntity({ type: "Model", @@ -314,7 +372,7 @@ var toolBar = (function () { var entityID = null; if (position != null) { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions), - properties.position = position; + properties.position = position; entityID = Entities.addEntity(properties); if (dragOnCreate) { @@ -329,12 +387,15 @@ var toolBar = (function () { var newModelButtonDown = false; var browseMarketplaceButtonDown = false; - that.mousePressEvent = function (event) { + that.mousePressEvent = function(event) { var clickedOverlay, url, file; - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (activeButton === toolBar.clicked(clickedOverlay)) { that.setActive(!isActive); @@ -360,7 +421,11 @@ var toolBar = (function () { createNewEntity({ type: "Box", dimensions: DEFAULT_DIMENSIONS, - color: { red: 255, green: 0, blue: 0 } + color: { + red: 255, + green: 0, + blue: 0 + } }); return true; @@ -370,7 +435,11 @@ var toolBar = (function () { createNewEntity({ type: "Sphere", dimensions: DEFAULT_DIMENSIONS, - color: { red: 255, green: 0, blue: 0 } + color: { + red: 255, + green: 0, + blue: 0 + } }); return true; @@ -381,7 +450,11 @@ var toolBar = (function () { type: "Light", dimensions: DEFAULT_LIGHT_DIMENSIONS, isSpotlight: false, - color: { red: 150, green: 150, blue: 150 }, + color: { + red: 150, + green: 150, + blue: 150 + }, constantAttenuation: 1, linearAttenuation: 0, @@ -396,9 +469,21 @@ var toolBar = (function () { if (newTextButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Text", - dimensions: { x: 0.65, y: 0.3, z: 0.01 }, - backgroundColor: { red: 64, green: 64, blue: 64 }, - textColor: { red: 255, green: 255, blue: 255 }, + dimensions: { + x: 0.65, + y: 0.3, + z: 0.01 + }, + backgroundColor: { + red: 64, + green: 64, + blue: 64 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, text: "some text", lineHeight: 0.06 }); @@ -409,7 +494,11 @@ var toolBar = (function () { if (newWebButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Web", - dimensions: { x: 1.6, y: 0.9, z: 0.01 }, + dimensions: { + x: 1.6, + y: 0.9, + z: 0.01 + }, sourceUrl: "https://highfidelity.com/", }); @@ -419,7 +508,11 @@ var toolBar = (function () { if (newZoneButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Zone", - dimensions: { x: 10, y: 10, z: 10 }, + dimensions: { + x: 10, + y: 10, + z: 10 + }, }); return true; @@ -428,27 +521,83 @@ var toolBar = (function () { if (newPolyVoxButton === toolBar.clicked(clickedOverlay)) { var polyVoxId = createNewEntity({ type: "PolyVox", - dimensions: { x: 10, y: 10, z: 10 }, - voxelVolumeSize: {x:16, y:16, z:16}, + dimensions: { + x: 10, + y: 10, + z: 10 + }, + voxelVolumeSize: { + x: 16, + y: 16, + z: 16 + }, voxelSurfaceStyle: 2 }); for (var x = 1; x <= 14; x++) { - Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: x, y: 1, z: 14}, 255); - Entities.setVoxel(polyVoxId, {x: x, y: 14, z: 14}, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 1, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 14, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 1, + z: 14 + }, 255); + Entities.setVoxel(polyVoxId, { + x: x, + y: 14, + z: 14 + }, 255); } for (var y = 2; y <= 13; y++) { - Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 1}, 255); - Entities.setVoxel(polyVoxId, {x: 1, y: y, z: 14}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: y, z: 14}, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: y, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: y, + z: 1 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: y, + z: 14 + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: y, + z: 14 + }, 255); } for (var z = 2; z <= 13; z++) { - Entities.setVoxel(polyVoxId, {x: 1, y: 1, z: z}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: 1, z: z}, 255); - Entities.setVoxel(polyVoxId, {x: 1, y: 14, z: z}, 255); - Entities.setVoxel(polyVoxId, {x: 14, y: 14, z: z}, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: 1, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: 1, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 1, + y: 14, + z: z + }, 255); + Entities.setVoxel(polyVoxId, { + x: 14, + y: 14, + z: z + }, 255); } @@ -461,7 +610,10 @@ var toolBar = (function () { that.mouseReleaseEvent = function(event) { var handled = false; if (newModelButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (newModelButton === toolBar.clicked(clickedOverlay)) { url = Window.prompt("Model URL", modelURLs[Math.floor(Math.random() * modelURLs.length)]); if (url !== null && url !== "") { @@ -470,7 +622,10 @@ var toolBar = (function () { handled = true; } } else if (browseMarketplaceButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { url = Window.s3Browse(".*(fbx|FBX|obj|OBJ)"); if (url !== null && url !== "") { @@ -497,7 +652,7 @@ var toolBar = (function () { } }); - that.cleanup = function () { + that.cleanup = function() { toolBar.cleanup(); }; @@ -568,17 +723,26 @@ function findClickedEntity(event) { } var foundEntity = result.entityID; - return { pickRay: pickRay, entityID: foundEntity }; + return { + pickRay: pickRay, + entityID: foundEntity + }; } var mouseHasMovedSincePress = false; var mousePressStartTime = 0; -var mousePressStartPosition = { x: 0, y: 0 }; +var mousePressStartPosition = { + x: 0, + y: 0 +}; var mouseDown = false; function mousePressEvent(event) { mouseDown = true; - mousePressStartPosition = { x: event.x, y: event.y }; + mousePressStartPosition = { + x: event.x, + y: event.y + }; mousePressStartTime = Date.now(); mouseHasMovedSincePress = false; mouseCapturedByTool = false; @@ -605,9 +769,11 @@ var IDLE_MOUSE_TIMEOUT = 200; var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; var lastMouseMoveEvent = null; + function mouseMoveEventBuffered(event) { lastMouseMoveEvent = event; } + function mouseMove(event) { if (mouseDown && !mouseHasMovedSincePress) { var timeSincePressMicro = Date.now() - mousePressStartTime; @@ -647,7 +813,10 @@ function mouseMove(event) { return; } - lastMousePosition = { x: event.x, y: event.y }; + lastMousePosition = { + x: event.x, + y: event.y + }; idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT); } @@ -660,22 +829,25 @@ function highlightEntityUnderCursor(position, accurateRay) { var pickRay = Camera.computePickRay(position.x, position.y); var entityIntersection = Entities.findRayIntersection(pickRay, accurateRay); if (entityIntersection.accurate) { - if(highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { + if (highlightedEntityID && highlightedEntityID != entityIntersection.entityID) { selectionDisplay.unhighlightSelectable(highlightedEntityID); - highlightedEntityID = { id: -1 }; + highlightedEntityID = { + id: -1 + }; } var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0; var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), - entityIntersection.properties.position)) * 180 / 3.14; + entityIntersection.properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (entityIntersection.entityID && sizeOK) { if (wantEntityGlow) { - Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 }); + Entities.editEntity(entityIntersection.entityID, { + glowLevel: 0.25 + }); } highlightedEntityID = entityIntersection.entityID; selectionDisplay.highlightSelectable(entityIntersection.entityID); @@ -693,18 +865,23 @@ function mouseReleaseEvent(event) { lastMouseMoveEvent = null; } if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) { + return true; } if (placingEntityID) { + if (isActive) { + selectionManager.setSelections([placingEntityID]); } placingEntityID = null; } if (isActive && selectionManager.hasSelection()) { + tooltip.show(false); } if (mouseCapturedByTool) { + return; } @@ -757,8 +934,7 @@ function mouseClickEvent(event) { var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; - var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) - && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); + var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE) && (allowSmallModels || angularSize > MIN_ANGULAR_SIZE); if (0 < x && sizeOK) { entitySelected = true; @@ -779,8 +955,8 @@ function mouseClickEvent(event) { if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } @@ -813,44 +989,125 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); // added it. var modelMenuAddedDelete = false; var originalLightsArePickable = Entities.getLightsArePickable(); + function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Models", + isSeparator: true, + beforeItem: "Physics" + }); if (!Menu.menuItemExists("Edit", "Delete")) { print("no delete... adding ours"); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Delete", - shortcutKeyEvent: { text: "backspace" }, afterItem: "Models" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Delete", + shortcutKeyEvent: { + text: "backspace" + }, + afterItem: "Models" + }); modelMenuAddedDelete = true; } else { print("delete exists... don't add ours"); } - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", - afterItem: "Entity List...", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", - afterItem: "Allow Selecting of Small Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities In Box", shortcutKey: "CTRL+SHIFT+META+A", - afterItem: "Allow Selecting of Lights" }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities Touching Box", shortcutKey: "CTRL+SHIFT+META+T", - afterItem: "Select All Entities In Box" }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Entity List...", + shortcutKey: "CTRL+META+L", + afterItem: "Models" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Large Models", + shortcutKey: "CTRL+META+L", + afterItem: "Entity List...", + isCheckable: true, + isChecked: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Small Models", + shortcutKey: "CTRL+META+S", + afterItem: "Allow Selecting of Large Models", + isCheckable: true, + isChecked: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Allow Selecting of Lights", + shortcutKey: "CTRL+SHIFT+META+L", + afterItem: "Allow Selecting of Small Models", + isCheckable: true + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities In Box", + shortcutKey: "CTRL+SHIFT+META+A", + afterItem: "Allow Selecting of Lights" + }); + Menu.addMenuItem({ + menuName: "Edit", + menuItemName: "Select All Entities Touching Box", + shortcutKey: "CTRL+SHIFT+META+T", + afterItem: "Select All Entities In Box" + }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" }); - Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Models", + isSeparator: true, + beforeItem: "Settings" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Export Entities", + shortcutKey: "CTRL+META+E", + afterItem: "Models" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Import Entities", + shortcutKey: "CTRL+META+I", + afterItem: "Export Entities" + }); + Menu.addMenuItem({ + menuName: "File", + menuItemName: "Import Entities from URL", + shortcutKey: "CTRL+META+U", + afterItem: "Import Entities" + }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_AUTO_FOCUS_ON_SELECT, - isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS, - isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" }); - Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, - isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_EASE_ON_FOCUS, + afterItem: MENU_AUTO_FOCUS_ON_SELECT, + isCheckable: true, + isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + afterItem: MENU_EASE_ON_FOCUS, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" + }); + Menu.addMenuItem({ + menuName: "View", + menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, + afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE, + isCheckable: true, + isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" + }); Entities.setLightsArePickable(false); } @@ -903,7 +1160,7 @@ var lastOrientation = null; var lastPosition = null; // Do some stuff regularly, like check for placement of various overlays -Script.update.connect(function (deltaTime) { +Script.update.connect(function(deltaTime) { progressDialog.move(); selectionDisplay.checkMove(); var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1); @@ -920,16 +1177,14 @@ Script.update.connect(function (deltaTime) { }); function insideBox(center, dimensions, point) { - return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) - && (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) - && (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); + return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0)) && (Math.abs(point.y - center.y) <= (dimensions.y / 2.0)) && (Math.abs(point.z - center.z) <= (dimensions.z / 2.0)); } function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { if (selectionManager.hasSelection()) { // Get all entities touching the bounding box of the current selection var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition, - Vec3.multiply(selectionManager.worldDimensions, 0.5)); + Vec3.multiply(selectionManager.worldDimensions, 0.5)); var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions); if (!keepIfTouching) { @@ -941,9 +1196,13 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { } else { isValid = function(position) { var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation), - Vec3.subtract(position, - selectionManager.localPosition)); - return insideBox({ x: 0, y: 0, z: 0 }, selectionManager.localDimensions, localPosition); + Vec3.subtract(position, + selectionManager.localPosition)); + return insideBox({ + x: 0, + y: 0, + z: 0 + }, selectionManager.localDimensions, localPosition); } } for (var i = 0; i < entities.length; ++i) { @@ -1038,15 +1297,11 @@ function getPositionToCreateEntity() { var placementPosition = Vec3.sum(Camera.position, offset); var cameraPosition = Camera.position; - + var HALF_TREE_SCALE = 16384; - var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE - || Math.abs(cameraPosition.y) > HALF_TREE_SCALE - || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; - var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE - || Math.abs(placementPosition.y) > HALF_TREE_SCALE - || Math.abs(placementPosition.z) > HALF_TREE_SCALE; + var cameraOutOfBounds = Math.abs(cameraPosition.x) > HALF_TREE_SCALE || Math.abs(cameraPosition.y) > HALF_TREE_SCALE || Math.abs(cameraPosition.z) > HALF_TREE_SCALE; + var placementOutOfBounds = Math.abs(placementPosition.x) > HALF_TREE_SCALE || Math.abs(placementPosition.y) > HALF_TREE_SCALE || Math.abs(placementPosition.z) > HALF_TREE_SCALE; if (cameraOutOfBounds && placementOutOfBounds) { return null; @@ -1065,14 +1320,22 @@ function importSVO(importURL) { return; } - Overlays.editOverlay(importingSVOTextOverlay, { visible: true }); - Overlays.editOverlay(importingSVOImageOverlay, { visible: true }); + Overlays.editOverlay(importingSVOTextOverlay, { + visible: true + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: true + }); var success = Clipboard.importEntities(importURL); if (success) { var VERY_LARGE = 10000; - var position = { x: 0, y: 0, z: 0 }; + var position = { + x: 0, + y: 0, + z: 0 + }; if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) { position = getPositionToCreateEntity(); } @@ -1091,8 +1354,12 @@ function importSVO(importURL) { Window.alert("There was an error importing the entity file."); } - Overlays.editOverlay(importingSVOTextOverlay, { visible: false }); - Overlays.editOverlay(importingSVOImageOverlay, { visible: false }); + Overlays.editOverlay(importingSVOTextOverlay, { + visible: false + }); + Overlays.editOverlay(importingSVOImageOverlay, { + visible: false + }); } Window.svoImportRequested.connect(importSVO); @@ -1104,7 +1371,7 @@ Controller.keyPressEvent.connect(function(event) { } }); -Controller.keyReleaseEvent.connect(function (event) { +Controller.keyReleaseEvent.connect(function(event) { if (isActive) { cameraManager.keyReleaseEvent(event); } @@ -1120,8 +1387,8 @@ Controller.keyReleaseEvent.connect(function (event) { if (selectionManager.hasSelection()) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } } else if (event.text == '[') { @@ -1131,7 +1398,11 @@ Controller.keyReleaseEvent.connect(function (event) { } else if (event.text == 'g') { if (isActive && selectionManager.hasSelection()) { var newPosition = selectionManager.worldPosition; - newPosition = Vec3.subtract(newPosition, { x: 0, y: selectionManager.worldDimensions.y * 0.5, z: 0 }); + newPosition = Vec3.subtract(newPosition, { + x: 0, + y: selectionManager.worldDimensions.y * 0.5, + z: 0 + }); grid.setPosition(newPosition); } } @@ -1140,8 +1411,7 @@ Controller.keyReleaseEvent.connect(function (event) { // When an entity has been deleted we need a way to "undo" this deletion. Because it's not currently // possible to create an entity with a specific id, earlier undo commands to the deleted entity // will fail if there isn't a way to find the new entity id. -DELETED_ENTITY_MAP = { -} +DELETED_ENTITY_MAP = {} function applyEntityProperties(data) { var properties = data.setProperties; @@ -1293,7 +1563,11 @@ PropertiesTool = function(opts) { if (selectionManager.hasSelection()) { selectionManager.saveProperties(); var dY = grid.getOrigin().y - (selectionManager.worldPosition.y - selectionManager.worldDimensions.y / 2); - var diff = { x: 0, y: dY, z: 0 }; + var diff = { + x: 0, + y: dY, + z: 0 + }; for (var i = 0; i < selectionManager.selections.length; i++) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; var newPosition = Vec3.sum(properties.position, diff); @@ -1311,7 +1585,11 @@ PropertiesTool = function(opts) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; var bottomY = properties.boundingBox.center.y - properties.boundingBox.dimensions.y / 2; var dY = grid.getOrigin().y - bottomY; - var diff = { x: 0, y: dY, z: 0 }; + var diff = { + x: 0, + y: dY, + z: 0 + }; var newPosition = Vec3.sum(properties.position, diff); Entities.editEntity(selectionManager.selections[i], { position: newPosition, @@ -1328,10 +1606,8 @@ PropertiesTool = function(opts) { var naturalDimensions = properties.naturalDimensions; // If any of the natural dimensions are not 0, resize - if (properties.type == "Model" && naturalDimensions.x == 0 - && naturalDimensions.y == 0 && naturalDimensions.z == 0) { - Window.alert("Cannot reset entity to its natural dimensions: Model URL" - + " is invalid or the model has not yet been loaded."); + if (properties.type == "Model" && naturalDimensions.x == 0 && naturalDimensions.y == 0 && naturalDimensions.z == 0) { + Window.alert("Cannot reset entity to its natural dimensions: Model URL" + " is invalid or the model has not yet been loaded."); } else { Entities.editEntity(selectionManager.selections[i], { dimensions: properties.naturalDimensions, @@ -1370,12 +1646,16 @@ PropertiesTool = function(opts) { var properties = selectionManager.savedProperties[selectionManager.selections[i]]; if (properties.type == "Zone") { var centerOfZone = properties.boundingBox.center; - var atmosphereCenter = { x: centerOfZone.x, - y: centerOfZone.y - properties.atmosphere.innerRadius, - z: centerOfZone.z }; + var atmosphereCenter = { + x: centerOfZone.x, + y: centerOfZone.y - properties.atmosphere.innerRadius, + z: centerOfZone.z + }; Entities.editEntity(selectionManager.selections[i], { - atmosphere: { center: atmosphereCenter }, + atmosphere: { + center: atmosphereCenter + }, }); } } @@ -1399,11 +1679,23 @@ PopupMenu = function() { var overlays = []; var overlayInfo = {}; - var upColor = { red: 0, green: 0, blue: 0 }; - var downColor = { red: 192, green: 192, blue: 192 }; - var overColor = { red: 128, green: 128, blue: 128 }; + var upColor = { + red: 0, + green: 0, + blue: 0 + }; + var downColor = { + red: 192, + green: 192, + blue: 192 + }; + var overColor = { + red: 128, + green: 128, + blue: 128 + }; - self.onSelectMenuItem = function() { }; + self.onSelectMenuItem = function() {}; self.addMenuItem = function(name) { var id = Overlays.addOverlay("text", { @@ -1414,16 +1706,22 @@ PopupMenu = function() { leftMargin: TEXT_MARGIN, width: 210, height: MENU_ITEM_HEIGHT, - font: { size: 12 }, + font: { + size: 12 + }, visible: false, }); overlays.push(id); - overlayInfo[id] = { name: name }; + overlayInfo[id] = { + name: name + }; return id; }; self.updateMenuItemText = function(id, newText) { - Overlays.editOverlay(id, { text: newText }); + Overlays.editOverlay(id, { + text: newText + }); }; self.setPosition = function(x, y) { @@ -1436,17 +1734,22 @@ PopupMenu = function() { } }; - self.onSelected = function() { }; + self.onSelected = function() {}; var pressingOverlay = null; var hoveringOverlay = null; self.mousePressEvent = function(event) { if (event.isLeftButton) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (overlay in overlayInfo) { pressingOverlay = overlay; - Overlays.editOverlay(pressingOverlay, { backgroundColor: downColor }); + Overlays.editOverlay(pressingOverlay, { + backgroundColor: downColor + }); } else { self.hide(); } @@ -1455,14 +1758,21 @@ PopupMenu = function() { }; self.mouseMoveEvent = function(event) { if (visible) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (!pressingOverlay) { if (hoveringOverlay != null && overlay != hoveringOverlay) { - Overlays.editOverlay(hoveringOverlay, { backgroundColor: upColor}); + Overlays.editOverlay(hoveringOverlay, { + backgroundColor: upColor + }); hoveringOverlay = null; } if (overlay != hoveringOverlay && overlay in overlayInfo) { - Overlays.editOverlay(overlay, { backgroundColor: overColor }); + Overlays.editOverlay(overlay, { + backgroundColor: overColor + }); hoveringOverlay = overlay; } } @@ -1470,12 +1780,17 @@ PopupMenu = function() { return false; }; self.mouseReleaseEvent = function(event) { - var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + var overlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (pressingOverlay != null) { if (overlay == pressingOverlay) { self.onSelectMenuItem(overlayInfo[overlay].name); } - Overlays.editOverlay(pressingOverlay, { backgroundColor: upColor }); + Overlays.editOverlay(pressingOverlay, { + backgroundColor: upColor + }); pressingOverlay = null; self.hide(); } @@ -1487,7 +1802,9 @@ PopupMenu = function() { if (newVisible != visible) { visible = newVisible; for (var key in overlayInfo) { - Overlays.editOverlay(key, { visible: newVisible }); + Overlays.editOverlay(key, { + visible: newVisible + }); } } } @@ -1527,4 +1844,4 @@ propertyMenu.onSelectMenuItem = function(name) { var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); -propertiesTool = PropertiesTool(); +propertiesTool = PropertiesTool(); \ No newline at end of file diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index a3b817e300..d304a6382e 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -118,8 +118,16 @@ CameraManager = function() { that.targetYaw = 0; that.targetPitch = 0; - that.focalPoint = { x: 0, y: 0, z: 0 }; - that.targetFocalPoint = { x: 0, y: 0, z: 0 }; + that.focalPoint = { + x: 0, + y: 0, + z: 0 + }; + that.targetFocalPoint = { + x: 0, + y: 0, + z: 0 + }; easing = false; easingTime = 0; @@ -127,13 +135,18 @@ CameraManager = function() { that.previousCameraMode = null; - that.lastMousePosition = { x: 0, y: 0 }; + that.lastMousePosition = { + x: 0, + y: 0 + }; that.enable = function() { if (Camera.mode == "independent" || that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.captureKeyEvents({ text: CAPTURED_KEYS[i] }); + Controller.captureKeyEvents({ + text: CAPTURED_KEYS[i] + }); } that.enabled = true; @@ -143,7 +156,7 @@ CameraManager = function() { that.zoomDistance = INITIAL_ZOOM_DISTANCE; that.targetZoomDistance = that.zoomDistance + 3.0; var focalPoint = Vec3.sum(Camera.getPosition(), - Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); + Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation()))); // Determine the correct yaw and pitch to keep the camera in the same location var dPos = Vec3.subtract(focalPoint, Camera.getPosition()); @@ -169,7 +182,9 @@ CameraManager = function() { if (!that.enabled) return; for (var i = 0; i < CAPTURED_KEYS.length; i++) { - Controller.releaseKeyEvents({ text: CAPTURED_KEYS[i] }); + Controller.releaseKeyEvents({ + text: CAPTURED_KEYS[i] + }); } that.enabled = false; @@ -335,19 +350,27 @@ CameraManager = function() { var hasDragged = false; that.mousePressEvent = function(event) { + if (cameraTool.mousePressEvent(event)) { + return true; } + if (!that.enabled) return; if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { + that.mode = MODE_ORBIT; } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { + + that.mode = MODE_PAN; } - if (that.mode != MODE_INACTIVE) { + if (that.mode !== MODE_INACTIVE) { + + hasDragged = false; return true; @@ -357,10 +380,12 @@ CameraManager = function() { } that.mouseReleaseEvent = function(event) { + if (!that.enabled) return; - Window.setCursorVisible(true); that.mode = MODE_INACTIVE; + Window.setCursorVisible(true); + } that.keyPressEvent = function(event) { @@ -396,15 +421,31 @@ CameraManager = function() { return; } - var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 }); - var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 }); + var yRot = Quat.angleAxis(that.yaw, { + x: 0, + y: 1, + z: 0 + }); + var xRot = Quat.angleAxis(that.pitch, { + x: 1, + y: 0, + z: 0 + }); var q = Quat.multiply(yRot, xRot); var pos = Vec3.multiply(Quat.getFront(q), that.zoomDistance); Camera.setPosition(Vec3.sum(that.focalPoint, pos)); - yRot = Quat.angleAxis(that.yaw - 180, { x: 0, y: 1, z: 0 }); - xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 }); + yRot = Quat.angleAxis(that.yaw - 180, { + x: 0, + y: 1, + z: 0 + }); + xRot = Quat.angleAxis(-that.pitch, { + x: 1, + y: 0, + z: 0 + }); q = Quat.multiply(yRot, xRot); if (easing) { @@ -483,7 +524,7 @@ CameraManager = function() { } }); - Controller.keyReleaseEvent.connect(function (event) { + Controller.keyReleaseEvent.connect(function(event) { if (event.text == "ESC" && that.enabled) { Camera.mode = lastAvatarCameraMode; cameraManager.disable(true); @@ -503,9 +544,21 @@ CameraManager = function() { CameraTool = function(cameraManager) { var that = {}; - var RED = { red: 191, green: 78, blue: 38 }; - var GREEN = { red: 26, green: 193, blue: 105 }; - var BLUE = { red: 0, green: 131, blue: 204 }; + var RED = { + red: 191, + green: 78, + blue: 38 + }; + var GREEN = { + red: 26, + green: 193, + blue: 105 + }; + var BLUE = { + red: 0, + green: 131, + blue: 204 + }; var BORDER_WIDTH = 1; @@ -513,10 +566,10 @@ CameraTool = function(cameraManager) { var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2; var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5, - var ORIENTATION_OVERLAY_OFFSET = { - x: 30, - y: 30, - } + var ORIENTATION_OVERLAY_OFFSET = { + x: 30, + y: 30, + } var UI_WIDTH = 70; var UI_HEIGHT = 70; @@ -536,7 +589,11 @@ CameraTool = function(cameraManager) { height: UI_HEIGHT + BORDER_WIDTH * 2, alpha: 0, text: "", - backgroundColor: { red: 101, green: 101, blue: 101 }, + backgroundColor: { + red: 101, + green: 101, + blue: 101 + }, backgroundAlpha: 1.0, visible: false, }); @@ -548,7 +605,11 @@ CameraTool = function(cameraManager) { height: UI_HEIGHT, alpha: 0, text: "", - backgroundColor: { red: 51, green: 51, blue: 51 }, + backgroundColor: { + red: 51, + green: 51, + blue: 51 + }, backgroundAlpha: 1.0, visible: false, }); @@ -556,7 +617,11 @@ CameraTool = function(cameraManager) { var defaultCubeProps = { size: ORIENTATION_OVERLAY_CUBE_SIZE, alpha: 1, - color: { red: 255, green: 0, blue: 0 }, + color: { + red: 255, + green: 0, + blue: 0 + }, solid: true, visible: true, drawOnHUD: true, @@ -564,10 +629,26 @@ CameraTool = function(cameraManager) { var defaultLineProps = { lineWidth: 1.5, alpha: 1, - position: { x: 0, y: 0, z: 0 }, - start: { x: 0, y: 0, z: 0 }, - end: { x: 0, y: 0, z: 0 }, - color: { red: 255, green: 0, blue: 0 }, + position: { + x: 0, + y: 0, + z: 0 + }, + start: { + x: 0, + y: 0, + z: 0 + }, + end: { + x: 0, + y: 0, + z: 0 + }, + color: { + red: 255, + green: 0, + blue: 0 + }, visible: false, drawOnHUD: true, }; @@ -582,30 +663,66 @@ CameraTool = function(cameraManager) { var OOHS = ORIENTATION_OVERLAY_HALF_SIZE; var cubeX = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: -OOHS, y: OOHS, z: OOHS }, + position: { + x: -OOHS, + y: OOHS, + z: OOHS + }, color: RED, })); var cubeY = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: OOHS, y: -OOHS, z: OOHS }, + position: { + x: OOHS, + y: -OOHS, + z: OOHS + }, color: GREEN, })); var cubeZ = orientationOverlay.createOverlay("cube", mergeObjects(defaultCubeProps, { - position: { x: OOHS, y: OOHS, z: -OOHS }, + position: { + x: OOHS, + y: OOHS, + z: -OOHS + }, color: BLUE, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: -OOHS, y: OOHS, z: OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: -OOHS, + y: OOHS, + z: OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: RED, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: OOHS, y: -OOHS, z: OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: OOHS, + y: -OOHS, + z: OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: GREEN, })); orientationOverlay.createOverlay("line3d", mergeObjects(defaultLineProps, { - start: { x: OOHS, y: OOHS, z: -OOHS }, - end: { x: OOHS, y: OOHS, z: OOHS }, + start: { + x: OOHS, + y: OOHS, + z: -OOHS + }, + end: { + x: OOHS, + y: OOHS, + z: OOHS + }, color: BLUE, })); @@ -645,7 +762,10 @@ CameraTool = function(cameraManager) { } that.mousePressEvent = function(event) { - var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); if (clickedOverlay == cubeX) { targetPitch = 0; @@ -666,12 +786,18 @@ CameraTool = function(cameraManager) { }; that.setVisible = function(visible) { - orientationOverlay.setProperties({ visible: visible }); - Overlays.editOverlay(background, { visible: visible }); - Overlays.editOverlay(backgroundBorder, { visible: visible }); + orientationOverlay.setProperties({ + visible: visible + }); + Overlays.editOverlay(background, { + visible: visible + }); + Overlays.editOverlay(backgroundBorder, { + visible: visible + }); }; that.setVisible(false); return that; -}; +}; \ No newline at end of file From 8d39f9c76059aab08bfabf672340273a669b9a5e Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 15:02:03 -0700 Subject: [PATCH 0026/1003] Syntax fixes --- .../painting/whiteboard/whiteboardEntityScript.js | 2 +- .../entities-renderer/src/EntityTreeRenderer.cpp | 4 ++-- .../entities-renderer/src/EntityTreeRenderer.h | 2 +- .../entities/src/EntityScriptingInterface.cpp | 14 +++++++------- libraries/entities/src/EntityScriptingInterface.h | 8 ++++---- libraries/octree/src/Octree.h | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index f9f272d5a3..139c92c710 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -52,7 +52,7 @@ origin: handPosition, direction: Quat.getUp(this.getHandRotation()) }; - this.intersection = Entities.findRayIntersection(pickRay, true); + this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); if (this.intersection.intersects) { if(JSON.stringify(this.intersection.entityID) === JSON.stringify(this.entityID)) { print('YAAAAA') diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fb9ab6563b..8f316af276 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -482,7 +482,7 @@ void EntityTreeRenderer::deleteReleasedModels() { } RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking) { + bool precisionPicking, const QVector& entityIdsToInclude) { RayToEntityIntersectionResult result; if (_tree) { EntityTreePointer entityTree = std::static_pointer_cast(_tree); @@ -490,7 +490,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, - result.face, result.surfaceNormal, + result.face, result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 08ff1ac296..15c030f77e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -130,7 +130,7 @@ private: QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking); + bool precisionPicking, const QVector& entityIdsToInclude = QVector()); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 47fee0ae4e..d260e73948 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -279,17 +279,17 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, const QVector& entityIdsToIgnore, bool precisionPicking) { - return findRayIntersectionWorker(ray, Octree::TryLock, entityIdsToIgnore, precisionPicking); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QVector& entityIdsToInclude) { + return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entityIdsToInclude); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, const QVector& entityIdsToIgnore, bool precisionPicking) { - return findRayIntersectionWorker(ray, Octree::Lock, entityIdsToIgnore, precisionPicking); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QVector& entityIdsToInclude) { + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entityIdsToInclude); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, - Octree::lockType lockType, const QVector& entityIdsToIgnore, - bool precisionPicking) { + Octree::lockType lockType, + bool precisionPicking, const QVector& entityIdsToInclude) { RayToEntityIntersectionResult result; @@ -297,7 +297,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, - result.surfaceNormal, (void**)&intersectedEntity, lockType, &result.accurate, + result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { result.entityID = intersectedEntity->getEntityItemID(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 632019a575..56610f2924 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -111,11 +111,11 @@ public slots: /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, const QVector& entityIdsToInclude, bool precisionPicking = false); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QVector& entityIdsToInclude = QVector()); /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, const QVector& entityIdsToInclude, bool precisionPicking = false); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QVector& entityIdsToInclude = QVector()); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; @@ -185,8 +185,8 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode - RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, const QVector& entityIdsToInclude, - bool precisionPicking); + RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, + bool precisionPicking, const QVector& entityIdsToInclude); EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 3ab1ad8ec4..f0a4d2c9c0 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -300,7 +300,7 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, + const QVector& entityIdsToInclude = QVector(), void** intersectedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL, From 6cbeb6e7c3a120b73601268c39da56bf6c691853 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 12 Oct 2015 15:40:16 -0700 Subject: [PATCH 0027/1003] Fix my bug, introduced in PR#6012, in which startup position goto got cancelled out. --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 22816e9001..605a81e9d9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -152,7 +152,7 @@ void MyAvatar::reset() { setEnableRigAnimations(true); // Reset dynamic state. - _wasPushing = _isPushing = _isBraking = _billboardValid = _goToPending = _straighteningLean = false; + _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false; _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); From f860ca923ef84a47cd93c25daf4e5ed2d367e121 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 12 Oct 2015 16:01:26 -0700 Subject: [PATCH 0028/1003] 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 ed9907ce66a2832cddac36f66f106778811b0f39 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 12 Oct 2015 16:23:12 -0700 Subject: [PATCH 0029/1003] do avatar simulation before physics simulation so that physics actions can update based on avatar --- 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 9e9830d977..739fefbc7f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2761,6 +2761,8 @@ void Application::update(float deltaTime) { updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... updateDialogs(deltaTime); // update various stats dialogs if present + _avatarUpdate->synchronousProcess(); + { PerformanceTimer perfTimer("physics"); myAvatar->relayDriveKeysToCharacterController(); @@ -2822,8 +2824,6 @@ void Application::update(float deltaTime) { _overlays.update(deltaTime); } - _avatarUpdate->synchronousProcess(); - // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera From 47471e837ff72a9c30812aa972d848bc85afea15 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 16:31:36 -0700 Subject: [PATCH 0030/1003] reset targets once they have stopped moving --- examples/toys/ping_pong_gun/createTargets.js | 15 ++++++++++++--- unpublishedScripts/hiddenEntityReset.js | 19 ++++++++++--------- unpublishedScripts/masterReset.js | 18 +++++++++--------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/examples/toys/ping_pong_gun/createTargets.js b/examples/toys/ping_pong_gun/createTargets.js index 22329f90f0..013150b241 100644 --- a/examples/toys/ping_pong_gun/createTargets.js +++ b/examples/toys/ping_pong_gun/createTargets.js @@ -16,8 +16,9 @@ var scriptURL = Script.resolvePath('wallTarget.js'); var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; +var MINIMUM_MOVE_LENGTH = 0.05; +var RESET_DISTANCE = 0.5; -var RESET_DISTANCE = 1; var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; var NUMBER_OF_TARGETS = 6; var TARGETS_PER_ROW = 3; @@ -60,6 +61,8 @@ var targets = []; var originalPositions = []; +var lastPositions = []; + function addTargets() { var i; var row = -1; @@ -77,6 +80,7 @@ function addTargets() { position.y = startPosition.y - (row * VERTICAL_SPACING); originalPositions.push(position); + lastPositions.push(position); var targetProperties = { name: 'Target', @@ -103,7 +107,11 @@ function testTargetDistanceFromStart() { var distance = Vec3.subtract(originalPosition, currentPosition); var length = Vec3.length(distance); - if (length > RESET_DISTANCE) { + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving RESET_DISTANCE) { + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { Entities.deleteEntity(target); @@ -482,15 +488,10 @@ compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) + script: scriptURL }; - var target = Entities.addEntity(targetProperties); - targets[index] = target; + + targets[index] = Entities.addEntity(targetProperties); } }); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index d9199a124d..e4db5fbf55 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -393,6 +393,7 @@ MasterReset = function() { var targets = []; var originalPositions = []; + var lastPositions = []; function addTargets() { var i; @@ -441,7 +442,11 @@ MasterReset = function() { var distance = Vec3.subtract(originalPosition, currentPosition); var length = Vec3.length(distance); - if (length > RESET_DISTANCE) { + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { Entities.deleteEntity(target); @@ -455,15 +460,10 @@ MasterReset = function() { compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) + script: scriptURL }; - var target = Entities.addEntity(targetProperties); - targets[index] = target; + + targets[index] = Entities.addEntity(targetProperties); } }); From bc4200d8d7b42eac3a5ebb422f394d66077093ea Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 16:35:06 -0700 Subject: [PATCH 0031/1003] push last positions to array --- unpublishedScripts/hiddenEntityReset.js | 5 +++-- unpublishedScripts/masterReset.js | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 5eff989701..bc9ecbcf22 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -438,8 +438,9 @@ var position = Vec3.sum(startPosition, multiplier); position.y = startPosition.y - (row * VERTICAL_SPACING); - originalPositions.push(position); - + originalPositions.push(position); + lastPositions.push(position); + var targetProperties = { name: 'Target', type: 'Model', diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index e4db5fbf55..1644688e5d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -411,7 +411,8 @@ MasterReset = function() { position.y = startPosition.y - (row * VERTICAL_SPACING); originalPositions.push(position); - + lastPositions.push(position); + var targetProperties = { name: 'Target', type: 'Model', @@ -429,8 +430,10 @@ MasterReset = function() { } }) }; + var target = Entities.addEntity(targetProperties); targets.push(target); + } } From f0ce698d78042e0b5313264fc1d152d64d274f52 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 12 Oct 2015 16:57:20 -0700 Subject: [PATCH 0032/1003] switch back to springy grab --- 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 05dfc8e5d5..f4bc7496da 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -480,7 +480,7 @@ function MyController(hand, triggerAction) { var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); this.actionID = NULL_ACTION_ID; - this.actionID = Entities.addAction("kinematic-hold", this.grabbedEntity, { + this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, From 02620c4b0dced96e067ffcedc39ebe51b0fe7f3f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 16:59:42 -0700 Subject: [PATCH 0033/1003] make targets ungrabbable --- examples/toys/ping_pong_gun/createTargets.js | 9 +- unpublishedScripts/hiddenEntityReset.js | 11 +- unpublishedScripts/masterReset.js | 2435 +++++++++--------- 3 files changed, 1233 insertions(+), 1222 deletions(-) diff --git a/examples/toys/ping_pong_gun/createTargets.js b/examples/toys/ping_pong_gun/createTargets.js index 013150b241..fb286b1928 100644 --- a/examples/toys/ping_pong_gun/createTargets.js +++ b/examples/toys/ping_pong_gun/createTargets.js @@ -111,7 +111,7 @@ function testTargetDistanceFromStart() { lastPositions[index] = currentPosition; - if (length > RESET_DISTANCE && moving RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { Entities.deleteEntity(target); @@ -125,7 +125,12 @@ function testTargetDistanceFromStart() { compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL + script: scriptURL, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) }; targets[index] = Entities.addEntity(targetProperties); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index bc9ecbcf22..f959923ad3 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -413,6 +413,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + grabbable: false } }) }); @@ -438,9 +441,9 @@ var position = Vec3.sum(startPosition, multiplier); position.y = startPosition.y - (row * VERTICAL_SPACING); - originalPositions.push(position); - lastPositions.push(position); - + originalPositions.push(position); + lastPositions.push(position); + var targetProperties = { name: 'Target', type: 'Model', @@ -455,7 +458,7 @@ userData: JSON.stringify({ resetMe: { resetMe: true - } + }, }) }; var target = Entities.addEntity(targetProperties); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 1644688e5d..fdedac3ec3 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -26,1256 +26,1259 @@ var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTa MasterReset = function() { - var resetKey = "resetMe"; - var GRABBABLE_DATA_KEY = "grabbableKey"; + var resetKey = "resetMe"; + var GRABBABLE_DATA_KEY = "grabbableKey"; - var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - var shouldDeleteOnEndScript = false; + var shouldDeleteOnEndScript = false; - //Before creating anything, first search a radius and delete all the things that should be deleted - deleteAllToys(); - createAllToys(); + //Before creating anything, first search a radius and delete all the things that should be deleted + deleteAllToys(); + createAllToys(); - function createAllToys() { - createBlocks({ - x: 548.3, - y: 495.55, - z: 504.4 - }); - - createBasketBall({ - x: 547.73, - y: 495.5, - z: 505.47 - }); - - createDoll({ - x: 546.67, - y: 495.41, - z: 505.09 - }); - - createWand({ - x: 546.71, - y: 495.55, - z: 506.15 - }); - - createDice(); - - createFlashlight({ - x: 545.72, - y: 495.41, - z: 505.78 - }); - - - - createCombinedArmChair({ - x: 549.29, - y: 494.9, - z: 508.22 - }); - - createPottedPlant({ - x: 554.26, - y: 495.2, - z: 504.53 - }); - - createPingPongBallGun(); - - createBasketballHoop(); - createBasketballRack(); - - createGates(); - - createFire(); - // Handles toggling of all sconce lights - createLights(); - - - - createCat({ - x: 551.09, - y: 494.98, - z: 503.49 - }); - - - createSprayCan({ - x: 549.7, - y: 495.6, - z: 503.91 - }); - - createTargets(); - - } - - function deleteAllToys() { - var entities = Entities.findEntities(MyAvatar.position, 100); - - entities.forEach(function(entity) { - //params: customKey, id, defaultValue - var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; - if (shouldReset === true) { - Entities.deleteEntity(entity); - } - }); - } - - function createFire() { - - - var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0); - - var animationSettings = JSON.stringify({ - fps: 30, - running: true, - loop: true, - firstFrame: 1, - lastFrame: 10000 - }); - - - var fire = Entities.addEntity({ - type: "ParticleEffect", - name: "fire", - animationSettings: animationSettings, - textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - position: { - x: 551.45, - y: 494.82, - z: 502.05 - }, - emitRate: 100, - colorStart: { - red: 70, - green: 70, - blue: 137 - }, - color: { - red: 200, - green: 99, - blue: 42 - }, - colorFinish: { - red: 255, - green: 99, - blue: 32 - }, - radiusSpread: 0.01, - radiusStart: 0.02, - radiusEnd: 0.001, - particleRadius: 0.05, - radiusFinish: 0.0, - emitOrientation: myOrientation, - emitSpeed: 0.3, - speedSpread: 0.1, - alphaStart: 0.05, - alpha: 0.1, - alphaFinish: 0.05, - emitDimensions: { - x: 1, - y: 1, - z: 0.1 - }, - polarFinish: 0.1, - emitAcceleration: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - accelerationSpread: { - x: 0.1, - y: 0.01, - z: 0.1 - }, - lifespan: 1, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - } - - function createBasketballRack() { - var NUMBER_OF_BALLS = 4; - var DIAMETER = 0.30; - var RESET_DISTANCE = 1; - var MINIMUM_MOVE_LENGTH = 0.05; - var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; - var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; - var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; - var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; - - var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); - - var rackStartPosition = { - x: 542.86, - y: 494.84, - z: 475.06 - }; - var rack = Entities.addEntity({ - name: 'Basketball Rack', - type: "Model", - modelURL: rackURL, - position: rackStartPosition, - rotation: rackRotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - linearDamping: 1, - dimensions: { - x: 0.4, - y: 1.37, - z: 1.73 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - compoundShapeURL: rackCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - - }) - }); - - var collidingBalls = []; - var originalBallPositions = []; - - function createCollidingBalls() { - var position = rackStartPosition; - - var i; - for (i = 0; i < NUMBER_OF_BALLS; i++) { - var ballPosition = { - x: position.x, - y: position.y + DIAMETER * 2, - z: position.z + (DIAMETER) - (DIAMETER * i) - }; - - var collidingBall = Entities.addEntity({ - type: "Model", - name: 'Colliding Basketball', - shapeType: 'Sphere', - position: { - x: position.x + (DIAMETER * 2) - (DIAMETER * i), - y: position.y + DIAMETER * 2, - z: position.z - }, - dimensions: { - x: DIAMETER, - y: DIAMETER, - z: DIAMETER - }, - restitution: 1.0, - linearDamping: 0.00001, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - modelURL: basketballURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) + function createAllToys() { + createBlocks({ + x: 548.3, + y: 495.55, + z: 504.4 }); - collidingBalls.push(collidingBall); - originalBallPositions.push(position); - } + createBasketBall({ + x: 547.73, + y: 495.5, + z: 505.47 + }); + + createDoll({ + x: 546.67, + y: 495.41, + z: 505.09 + }); + + createWand({ + x: 546.71, + y: 495.55, + z: 506.15 + }); + + createDice(); + + createFlashlight({ + x: 545.72, + y: 495.41, + z: 505.78 + }); + + + + createCombinedArmChair({ + x: 549.29, + y: 494.9, + z: 508.22 + }); + + createPottedPlant({ + x: 554.26, + y: 495.2, + z: 504.53 + }); + + createPingPongBallGun(); + + createBasketballHoop(); + createBasketballRack(); + + createGates(); + + createFire(); + // Handles toggling of all sconce lights + createLights(); + + + + createCat({ + x: 551.09, + y: 494.98, + z: 503.49 + }); + + + createSprayCan({ + x: 549.7, + y: 495.6, + z: 503.91 + }); + + createTargets(); + } - function testBallDistanceFromStart() { - var resetCount = 0; + function deleteAllToys() { + var entities = Entities.findEntities(MyAvatar.position, 100); - collidingBalls.forEach(function(ball, index) { - var currentPosition = Entities.getEntityProperties(ball, "position").position; - var originalPosition = originalBallPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); + entities.forEach(function(entity) { + //params: customKey, id, defaultValue + var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; + if (shouldReset === true) { + Entities.deleteEntity(entity); + } + }); + } - if (length > RESET_DISTANCE) { - Script.setTimeout(function() { - var newPosition = Entities.getEntityProperties(ball, "position").position; - var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); - if (moving < MINIMUM_MOVE_LENGTH) { - resetCount++; - if (resetCount === NUMBER_OF_BALLS) { + function createFire() { + + + var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0); + + var animationSettings = JSON.stringify({ + fps: 30, + running: true, + loop: true, + firstFrame: 1, + lastFrame: 10000 + }); + + + var fire = Entities.addEntity({ + type: "ParticleEffect", + name: "fire", + animationSettings: animationSettings, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + position: { + x: 551.45, + y: 494.82, + z: 502.05 + }, + emitRate: 100, + colorStart: { + red: 70, + green: 70, + blue: 137 + }, + color: { + red: 200, + green: 99, + blue: 42 + }, + colorFinish: { + red: 255, + green: 99, + blue: 32 + }, + radiusSpread: 0.01, + radiusStart: 0.02, + radiusEnd: 0.001, + particleRadius: 0.05, + radiusFinish: 0.0, + emitOrientation: myOrientation, + emitSpeed: 0.3, + speedSpread: 0.1, + alphaStart: 0.05, + alpha: 0.1, + alphaFinish: 0.05, + emitDimensions: { + x: 1, + y: 1, + z: 0.1 + }, + polarFinish: 0.1, + emitAcceleration: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + accelerationSpread: { + x: 0.1, + y: 0.01, + z: 0.1 + }, + lifespan: 1, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + } + + function createBasketballRack() { + var NUMBER_OF_BALLS = 4; + var DIAMETER = 0.30; + var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; + var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; + var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; + + var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); + + var rackStartPosition = { + x: 542.86, + y: 494.84, + z: 475.06 + }; + var rack = Entities.addEntity({ + name: 'Basketball Rack', + type: "Model", + modelURL: rackURL, + position: rackStartPosition, + rotation: rackRotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + linearDamping: 1, + dimensions: { + x: 0.4, + y: 1.37, + z: 1.73 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + compoundShapeURL: rackCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + + }) + }); + + var collidingBalls = []; + var originalBallPositions = []; + + function createCollidingBalls() { + var position = rackStartPosition; + + var i; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + var ballPosition = { + x: position.x, + y: position.y + DIAMETER * 2, + z: position.z + (DIAMETER) - (DIAMETER * i) + }; + + var collidingBall = Entities.addEntity({ + type: "Model", + name: 'Colliding Basketball', + shapeType: 'Sphere', + position: { + x: position.x + (DIAMETER * 2) - (DIAMETER * i), + y: position.y + DIAMETER * 2, + z: position.z + }, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + modelURL: basketballURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + + collidingBalls.push(collidingBall); + originalBallPositions.push(position); + } + } + + function testBallDistanceFromStart() { + var resetCount = 0; + + collidingBalls.forEach(function(ball, index) { + var currentPosition = Entities.getEntityProperties(ball, "position").position; + var originalPosition = originalBallPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + + if (length > RESET_DISTANCE) { + Script.setTimeout(function() { + var newPosition = Entities.getEntityProperties(ball, "position").position; + var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); + if (moving < MINIMUM_MOVE_LENGTH) { + resetCount++; + if (resetCount === NUMBER_OF_BALLS) { + deleteCollidingBalls(); + createCollidingBalls(); + } + } + }, 200); + } + }); + } + + function deleteEntity(entityID) { + if (entityID === rack) { deleteCollidingBalls(); - createCollidingBalls(); - } + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); } - }, 200); - } - }); - } - - function deleteEntity(entityID) { - if (entityID === rack) { - deleteCollidingBalls(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteCollidingBalls() { - while (collidingBalls.length > 0) { - Entities.deleteEntity(collidingBalls.pop()); - } - } - - createCollidingBalls(); - Entities.deletingEntity.connect(deleteEntity); - - var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); - } - - function createTargets() { - - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - - var RESET_DISTANCE = 1; - var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; - var NUMBER_OF_TARGETS = 6; - var TARGETS_PER_ROW = 3; - - var TARGET_DIMENSIONS = { - x: 0.06, - y: 0.42, - z: 0.42 - }; - - var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; - var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; - - - var startPosition = { - x: 548.68, - y: 497.30, - z: 509.74 - }; - - var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); - - var targetIntervalClearer = Entities.addEntity({ - name: 'Target Interval Clearer - delete me to clear', - type: 'Box', - position: startPosition, - dimensions: TARGET_DIMENSIONS, - rotation: rotation, - visible: false, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - - var targets = []; - - var originalPositions = []; - var lastPositions = []; - - function addTargets() { - var i; - var row = -1; - for (i = 0; i < NUMBER_OF_TARGETS; i++) { - - if (i % TARGETS_PER_ROW === 0) { - row++; } - var vHat = Quat.getFront(rotation); - var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); - var multiplier = Vec3.multiply(spacer, vHat); - var position = Vec3.sum(startPosition, multiplier); - position.y = startPosition.y - (row * VERTICAL_SPACING); - - originalPositions.push(position); - lastPositions.push(position); - - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: position, - rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true + function deleteCollidingBalls() { + while (collidingBalls.length > 0) { + Entities.deleteEntity(collidingBalls.pop()); } - }) + } + + createCollidingBalls(); + Entities.deletingEntity.connect(deleteEntity); + + var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); + } + + function createTargets() { + + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; + + var RESET_DISTANCE = 1; + var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; + var NUMBER_OF_TARGETS = 6; + var TARGETS_PER_ROW = 3; + + var TARGET_DIMENSIONS = { + x: 0.06, + y: 0.42, + z: 0.42 }; - var target = Entities.addEntity(targetProperties); - targets.push(target); + var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; + var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; + + + var startPosition = { + x: 548.68, + y: 497.30, + z: 509.74 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); + + var targetIntervalClearer = Entities.addEntity({ + name: 'Target Interval Clearer - delete me to clear', + type: 'Box', + position: startPosition, + dimensions: TARGET_DIMENSIONS, + rotation: rotation, + visible: false, + collisionsWillMove: false, + ignoreForCollisions: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + + var targets = []; + + var originalPositions = []; + var lastPositions = []; + + function addTargets() { + var i; + var row = -1; + for (i = 0; i < NUMBER_OF_TARGETS; i++) { + + if (i % TARGETS_PER_ROW === 0) { + row++; + } + + var vHat = Quat.getFront(rotation); + var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); + var multiplier = Vec3.multiply(spacer, vHat); + var position = Vec3.sum(startPosition, multiplier); + position.y = startPosition.y - (row * VERTICAL_SPACING); + + originalPositions.push(position); + lastPositions.push(position); + + var targetProperties = { + name: 'Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: position, + rotation: rotation, + script: targetsScriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }; + + var target = Entities.addEntity(targetProperties); + targets.push(target); + + } + } + + function testTargetDistanceFromStart() { + targets.forEach(function(target, index) { + + var currentPosition = Entities.getEntityProperties(target, "position").position; + var originalPosition = originalPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + + lastPositions[index] = currentPosition; + + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { + + Entities.deleteEntity(target); + + var targetProperties = { + name: 'Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: originalPositions[index], + rotation: rotation, + script: scriptURL + }; + + targets[index] = Entities.addEntity(targetProperties); + + } + }); + } + + + function deleteEntity(entityID) { + if (entityID === targetIntervalClearer) { + deleteTargets(); + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); + } + } + + function deleteTargets() { + while (targets.length > 0) { + Entities.deleteEntity(targets.pop()); + } + Entities.deleteEntity(targetIntervalClearer); + } + + Entities.deletingEntity.connect(deleteEntity); + var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); + + addTargets(); - } } - function testTargetDistanceFromStart() { - targets.forEach(function(target, index) { + function createCat(position) { - var currentPosition = Entities.getEntityProperties(target, "position").position; - var originalPosition = originalPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; + var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; + var animationSettings = JSON.stringify({ + running: true, + }); + var cat = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "cat", + script: catScriptURL, + animationURL: animationURL, + animationSettings: animationSettings, + position: position, + rotation: { + w: 0.35020983219146729, + x: -4.57763671875e-05, + y: 0.93664455413818359, + z: -1.52587890625e-05 + }, + dimensions: { + x: 0.15723302960395813, + y: 0.50762706995010376, + z: 0.90716040134429932 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); - var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + } - lastPositions[index] = currentPosition; + function createFlashlight(position) { + var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { + var flashlight = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "flashlight", + script: flashlightScriptURL, + position: position, + dimensions: { + x: 0.08, + y: 0.30, + z: 0.08 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: 'box', + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); - Entities.deleteEntity(target); - var targetProperties = { - name: 'Target', + } + + function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; + + + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: lightsScriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Hall Light" + } + }) + }); + + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); + + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); + + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: lightsScriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Garage Light" + } + }) + }); + + + + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, + + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + } + + + + function createDice() { + var diceProps = { + type: "Model", + modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", + name: "dice", + position: { + x: 541, + y: 494.96, + z: 509.1 + }, + dimensions: { + x: 0.09, + y: 0.09, + z: 0.09 + }, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: "box", + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }; + var dice1 = Entities.addEntity(diceProps); + + diceProps.position = { + x: 541.05, + y: 494.96, + z: 509.0 + }; + + var dice2 = Entities.addEntity(diceProps); + + } + + + function createGates() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + + var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); + var gate = Entities.addEntity({ + name: 'Front Door Fence', type: 'Model', modelURL: MODEL_URL, + shapeType: 'box', + position: { + x: 531.15, + y: 495.11, + z: 520.20 + }, + dimensions: { + x: 1.42, + y: 1.13, + z: 0.2 + }, + rotation: rotation, + collisionsWillMove: true, + gravity: { + x: 0, + y: -100, + z: 0 + }, + linearDamping: 1, + angularDamping: 10, + mass: 10, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createPingPongBallGun() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + + var position = { + x: 548.6, + y: 495.4, + z: 503.39 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + + var pingPongGun = Entities.addEntity({ + type: "Model", + modelURL: MODEL_URL, + shapeType: 'box', + script: pingPongScriptURL, + position: position, + rotation: rotation, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.08, + y: 0.21, + z: 0.47 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + } + + function createBasketballHoop() { + var position = { + x: 539.23, + y: 496.13, + z: 475.89 + }; + var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); + + var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; + var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + + var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: position, + rotation: rotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createWand(position) { + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; + + var entity = Entities.addEntity({ + name: 'Bubble Wand', + type: "Model", + modelURL: WAND_MODEL, + position: position, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.05, + y: 0.25, + z: 0.05 + }, + //must be enabled to be grabbable in the physics engine shapeType: 'compound', collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: originalPositions[index], - rotation: rotation, - script: scriptURL - }; - - targets[index] = Entities.addEntity(targetProperties); - - } - }); - } - - - function deleteEntity(entityID) { - if (entityID === targetIntervalClearer) { - deleteTargets(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteTargets() { - while (targets.length > 0) { - Entities.deleteEntity(targets.pop()); - } - Entities.deleteEntity(targetIntervalClearer); - } - - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); - - addTargets(); - - } - - function createCat(position) { - - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; - var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; - var animationSettings = JSON.stringify({ - running: true, - }); - var cat = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "cat", - script: catScriptURL, - animationURL: animationURL, - animationSettings: animationSettings, - position: position, - rotation: { - w: 0.35020983219146729, - x: -4.57763671875e-05, - y: 0.93664455413818359, - z: -1.52587890625e-05 - }, - dimensions: { - x: 0.15723302960395813, - y: 0.50762706995010376, - z: 0.90716040134429932 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - - } - - function createFlashlight(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - - var flashlight = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "flashlight", - script: flashlightScriptURL, - position: position, - dimensions: { - x: 0.08, - y: 0.30, - z: 0.08 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: 'box', - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - - - } - - function createLights() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; - - - var rotation = { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: lightsScriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Hall Light" - } - }) - }); - - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - rotation = { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }; - axis = { - x: 0, - y: 1, - z: 0 - }; - dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: lightsScriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Garage Light" - } - }) - }); - - - - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, - - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - } - - - - function createDice() { - var diceProps = { - type: "Model", - modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", - name: "dice", - position: { - x: 541, - y: 494.96, - z: 509.1 - }, - dimensions: { - x: 0.09, - y: 0.09, - z: 0.09 - }, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: "box", - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }; - var dice1 = Entities.addEntity(diceProps); - - diceProps.position = { - x: 541.05, - y: 494.96, - z: 509.0 - }; - - var dice2 = Entities.addEntity(diceProps); - - } - - - function createGates() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; - - var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); - var gate = Entities.addEntity({ - name: 'Front Door Fence', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'box', - position: { - x: 531.15, - y: 495.11, - z: 520.20 - }, - dimensions: { - x: 1.42, - y: 1.13, - z: 0.2 - }, - rotation: rotation, - collisionsWillMove: true, - gravity: { - x: 0, - y: -100, - z: 0 - }, - linearDamping: 1, - angularDamping: 10, - mass: 10, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - function createPingPongBallGun() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - - var position = { - x: 548.6, - y: 495.4, - z: 503.39 - }; - - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); - - var pingPongGun = Entities.addEntity({ - type: "Model", - modelURL: MODEL_URL, - shapeType: 'box', - script: pingPongScriptURL, - position: position, - rotation: rotation, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 0.08, - y: 0.21, - z: 0.47 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - } - - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - - var entity = Entities.addEntity({ - name: 'Bubble Wand', - type: "Model", - modelURL: WAND_MODEL, - position: position, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 0.05, - y: 0.25, - z: 0.05 - }, - //must be enabled to be grabbable in the physics engine - shapeType: 'compound', - collisionsWillMove: true, - compoundShapeURL: WAND_COLLISION_SHAPE, - //Look into why bubble wand is going through table when gravity is enabled - // gravity: {x: 0, y: -3.5, z: 0}, - // velocity: {x: 0, y: -0.01, z:0}, - script: wandScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - - } - - function createBasketBall(position) { - - var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: position, - collisionsWillMove: true, - shapeType: "sphere", - name: "basketball", - dimensions: { - x: 0.25, - y: 0.26, - z: 0.25 - }, - gravity: { - x: 0, - y: -7, - z: 0 - }, - restitution: 10, - linearDamping: 0.0, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createDoll(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; - - var naturalDimensions = { - x: 1.63, - y: 1.67, - z: 0.26 - }; - var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - var entity = Entities.addEntity({ - type: "Model", - name: "doll", - modelURL: modelURL, - script: dollScriptURL, - position: position, - shapeType: 'box', - dimensions: desiredDimensions, - gravity: { - x: 0, - y: -5, - z: 0 - }, - velocity: { - x: 0, - y: -0.1, - z: 0 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createSprayCan(position) { - - var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "spraycan", - script: sprayPaintScriptURL, - modelURL: modelURL, - position: position, - dimensions: { - x: 0.07, - y: 0.17, - z: 0.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -0.5, - z: 0 - }, - velocity: { - x: 0, - y: -1, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createPottedPlant(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "Potted Plant", - modelURL: modelURL, - position: position, - dimensions: { - x: 1.10, - y: 2.18, - z: 1.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.4, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - - function createCombinedArmChair(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; - var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; - - var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); - - var entity = Entities.addEntity({ - type: "Model", - name: "Red Arm Chair", - modelURL: modelURL, - shapeType: 'compound', - compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, - position: position, - rotation: rotation, - dimensions: { - x: 1.26, - y: 1.56, - z: 1.35 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -0.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.2, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - - } - - function createBlocks(position) { - var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; - var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; - var NUM_BLOCKS_PER_COLOR = 4; - var i, j; - - var blockTypes = [{ - url: "planky_blue.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_green.fbx", - dimensions: { - x: 0.1, - y: 0.1, - z: 0.25 - } - }, { - url: "planky_natural.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.05 - } - }, { - url: "planky_yellow.fbx", - dimensions: { - x: 0.03, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_red.fbx", - dimensions: { - x: 0.1, - y: 0.05, - z: 0.25 - } - }, ]; - - var modelURL, entity; - for (i = 0; i < blockTypes.length; i++) { - for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { - modelURL = baseURL + blockTypes[i].url; - entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: Vec3.sum(position, { - x: j / 10, - y: i / 10, - z: 0 - }), - shapeType: 'box', - name: "block", - dimensions: blockTypes[i].dimensions, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - gravity: { - x: 0, - y: -2.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) + compoundShapeURL: WAND_COLLISION_SHAPE, + //Look into why bubble wand is going through table when gravity is enabled + // gravity: {x: 0, y: -3.5, z: 0}, + // velocity: {x: 0, y: -0.01, z:0}, + script: wandScriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) }); - } + } - } - function cleanup() { - deleteAllToys(); - } + function createBasketBall(position) { - if (shouldDeleteOnEndScript) { + var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - Script.scriptEnding.connect(cleanup); - } + var entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: position, + collisionsWillMove: true, + shapeType: "sphere", + name: "basketball", + dimensions: { + x: 0.25, + y: 0.26, + z: 0.25 + }, + gravity: { + x: 0, + y: -7, + z: 0 + }, + restitution: 10, + linearDamping: 0.0, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + + function createDoll(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; + + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + var entity = Entities.addEntity({ + type: "Model", + name: "doll", + modelURL: modelURL, + script: dollScriptURL, + position: position, + shapeType: 'box', + dimensions: desiredDimensions, + gravity: { + x: 0, + y: -5, + z: 0 + }, + velocity: { + x: 0, + y: -0.1, + z: 0 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + + function createSprayCan(position) { + + var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "spraycan", + script: sprayPaintScriptURL, + modelURL: modelURL, + position: position, + dimensions: { + x: 0.07, + y: 0.17, + z: 0.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -0.5, + z: 0 + }, + velocity: { + x: 0, + y: -1, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + + function createPottedPlant(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "Potted Plant", + modelURL: modelURL, + position: position, + dimensions: { + x: 1.10, + y: 2.18, + z: 1.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.4, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + + function createCombinedArmChair(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; + var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; + + var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); + + var entity = Entities.addEntity({ + type: "Model", + name: "Red Arm Chair", + modelURL: modelURL, + shapeType: 'compound', + compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, + position: position, + rotation: rotation, + dimensions: { + x: 1.26, + y: 1.56, + z: 1.35 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -0.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.2, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + + } + + function createBlocks(position) { + var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var NUM_BLOCKS_PER_COLOR = 4; + var i, j; + + var blockTypes = [{ + url: "planky_blue.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_green.fbx", + dimensions: { + x: 0.1, + y: 0.1, + z: 0.25 + } + }, { + url: "planky_natural.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + } + }, { + url: "planky_yellow.fbx", + dimensions: { + x: 0.03, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_red.fbx", + dimensions: { + x: 0.1, + y: 0.05, + z: 0.25 + } + }, ]; + + var modelURL, entity; + for (i = 0; i < blockTypes.length; i++) { + for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { + modelURL = baseURL + blockTypes[i].url; + entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: Vec3.sum(position, { + x: j / 10, + y: i / 10, + z: 0 + }), + shapeType: 'box', + name: "block", + dimensions: blockTypes[i].dimensions, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + gravity: { + x: 0, + y: -2.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + + } + } + } + + function cleanup() { + deleteAllToys(); + } + + if (shouldDeleteOnEndScript) { + + Script.scriptEnding.connect(cleanup); + } }; \ No newline at end of file From fc8a4f4f2dba746f0883e606dae60ef8dcd114e3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 17:00:49 -0700 Subject: [PATCH 0034/1003] cleanup --- unpublishedScripts/masterReset.js | 1 - 1 file changed, 1 deletion(-) diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index fdedac3ec3..03dacef0a4 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -247,7 +247,6 @@ MasterReset = function() { grabbableKey: { grabbable: false } - }) }); From 0f998e81afd9f123083639e2d986220c689ba5fc Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 12 Oct 2015 17:03:32 -0700 Subject: [PATCH 0035/1003] add min reset distance --- unpublishedScripts/hiddenEntityReset.js | 4 ++-- unpublishedScripts/masterReset.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index f959923ad3..702aaec378 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -378,7 +378,8 @@ var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var RESET_DISTANCE = 0.5; var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; var NUMBER_OF_TARGETS = 6; var TARGETS_PER_ROW = 3; @@ -392,7 +393,6 @@ var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; - var startPosition = { x: 548.68, y: 497.30, diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 03dacef0a4..a6c3bfdd16 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -350,7 +350,8 @@ MasterReset = function() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var RESET_DISTANCE = 0.5; var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; var NUMBER_OF_TARGETS = 6; var TARGETS_PER_ROW = 3; From f93b1d3325bd12a1f0f1c8e7a08161f6936dc908 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 17:05:34 -0700 Subject: [PATCH 0036/1003] Correctly whitelisting entities for ray picking --- examples/controllers/handControllerGrab.js | 3 ++- .../whiteboard/whiteboardEntityScript.js | 7 ++---- .../painting/whiteboard/whiteboardSpawner.js | 4 +-- .../entities/src/EntityScriptingInterface.cpp | 25 ++++++++++++------- .../entities/src/EntityScriptingInterface.h | 4 +-- libraries/entities/src/EntityTreeElement.cpp | 14 ++++++++++- libraries/shared/src/RegisteredMetaTypes.cpp | 17 +++++++++++++ libraries/shared/src/RegisteredMetaTypes.h | 2 ++ 8 files changed, 56 insertions(+), 20 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 26cb522f9a..562e5396b1 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -253,6 +253,7 @@ function MyController(hand, triggerAction) { return; } + // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); var pickRay = { @@ -568,7 +569,7 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } - + print("AHHHHHH") Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 139c92c710..6d651c974e 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -22,6 +22,7 @@ var TIP_CONTROLLER_OFFSET = 1; Whiteboard = function() { _this = this; + print("WAAAAAH"); }; Whiteboard.prototype = { @@ -52,12 +53,8 @@ origin: handPosition, direction: Quat.getUp(this.getHandRotation()) }; + this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); - if (this.intersection.intersects) { - if(JSON.stringify(this.intersection.entityID) === JSON.stringify(this.entityID)) { - print('YAAAAA') - } - } }, releaseGrab: function() { diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 31a72212c3..506323dd59 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -15,7 +15,7 @@ Script.include("../../libraries/utils.js"); -var scriptURL = Script.resolvePath("whiteBoardEntityScript.js"); +var scriptURL = Script.resolvePath("whiteBoardEntityScript.js?v1" +Math.random()); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); @@ -26,7 +26,7 @@ var whiteboard = Entities.addEntity({ position: center, rotation: rotation, script: scriptURL, - dimensions: {x: 2, y: 1.5, z: 0.01}, + dimensions: {x: 2, y: 1.5, z: 0.1}, color: {red: 255, green: 255, blue: 255} }); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d260e73948..692ca7a638 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -279,12 +279,14 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QVector& entityIdsToInclude) { - return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entityIdsToInclude); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { + QVector entities = qVectorQUuidFromScriptValue(entityIdsToInclude); + return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QVector& entityIdsToInclude) { - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entityIdsToInclude); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { + const QVector& entities = qVectorQUuidFromScriptValue(entityIdsToInclude); + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, @@ -414,15 +416,20 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra QString faceName = object.property("face").toVariant().toString(); if (faceName == "MIN_X_FACE") { value.face = MIN_X_FACE; - } else if (faceName == "MAX_X_FACE") { + } + else if (faceName == "MAX_X_FACE") { value.face = MAX_X_FACE; - } else if (faceName == "MIN_Y_FACE") { + } + else if (faceName == "MIN_Y_FACE") { value.face = MIN_Y_FACE; - } else if (faceName == "MAX_Y_FACE") { + } + else if (faceName == "MAX_Y_FACE") { value.face = MAX_Y_FACE; - } else if (faceName == "MIN_Z_FACE") { + } + else if (faceName == "MIN_Z_FACE") { value.face = MIN_Z_FACE; - } else { + } + else { value.face = MAX_Z_FACE; }; QScriptValue intersection = object.property("intersection"); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 56610f2924..48f13e81bd 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -111,11 +111,11 @@ public slots: /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QVector& entityIdsToInclude = QVector()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QVector& entityIdsToInclude = QVector()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index d1f7adef94..89e8e2d6d8 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -501,8 +501,20 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con int entityNumber = 0; bool somethingIntersected = false; forEachEntity([&](EntityItemPointer entity) { + if (entityIdsToInclude.size() > 0) { + bool entityInWhiteList = false; + //We only want to search whitelist if there is one, otherwise everything except blacklisted items are valid + for (int i = 0; i < entityIdsToInclude.size(); i++) { + if (entityIdsToInclude.at(i) == entity->getID()) { + entityInWhiteList = true; + } + } + if (!entityInWhiteList) { + // qDebug() << "entity not found in whitelist!"; + return; + } + } - qDebug() << "entity Ids to ignore:************* " << entityIdsToInclude; AABox entityBox = entity->getAABox(); float localDistance; BoxFace localFace; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index b2389f4db6..27921336cf 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -104,6 +104,23 @@ QVector qVectorFloatFromScriptValue(const QScriptValue& array) { return newVector; } +QVector qVectorQUuidFromScriptValue(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(); + QUuid 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 c419741c3b..cd1e3b0d3e 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -65,6 +65,8 @@ QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector& vector); QVector qVectorFloatFromScriptValue(const QScriptValue& array); +QVector qVectorQUuidFromScriptValue(const QScriptValue& array); + class PickRay { public: PickRay() : origin(0.0f), direction(0.0f) { } From 6a8ff676ed3c979d3c441ad72405a35f7bba8cac Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 12 Oct 2015 17:32:18 -0700 Subject: [PATCH 0037/1003] Remove overlay hiding from middle mouse button. Because it conflicts with edit.js camera panning. You can still toggle the overlay via the O key. --- interface/src/Application.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9a6e2fff4d..8705c7b61d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1890,16 +1890,6 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { computePickRay(mappedEvent.x(), mappedEvent.y())); sendEvent(this, &actionEvent); - } else if (event->button() == Qt::RightButton) { - // "right click" on controllers to toggle the overlay - if (deviceID > 0) { - _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); - } - } else if (event->button() == Qt::MiddleButton) { - // mouse middle click to toggle the overlay - if (deviceID == 0) { - _overlayConductor.setEnabled(!_overlayConductor.getEnabled()); - } } } } From fb040426eb956c4781c730f1dc9ed67c855780e3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 17:34:50 -0700 Subject: [PATCH 0038/1003] added explanatory comment --- examples/painting/whiteboard/whiteboardEntityScript.js | 1 + examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 6d651c974e..202f296b6e 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -55,6 +55,7 @@ }; this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); + // this.intersection = Entities.findRayIntersection(pickRay, true); }, releaseGrab: function() { diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 506323dd59..058d2408a7 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -15,7 +15,7 @@ Script.include("../../libraries/utils.js"); -var scriptURL = Script.resolvePath("whiteBoardEntityScript.js?v1" +Math.random()); +var scriptURL = Script.resolvePath("whiteBoardEntityScript.js?v2"); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); From 33ff21f3766386cf94f5bbc7be642e25d854c88b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 17:36:07 -0700 Subject: [PATCH 0039/1003] Got rid of debug message in handControllerGrab script --- examples/controllers/handControllerGrab.js | 1 - examples/painting/whiteboard/whiteboardEntityScript.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 562e5396b1..9bb293126a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -569,7 +569,6 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } - print("AHHHHHH") Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 202f296b6e..1754bbe6ed 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -55,6 +55,7 @@ }; this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); + // Comment above line and uncomment below line to test difference in judder when whitelist is and is not provided // this.intersection = Entities.findRayIntersection(pickRay, true); }, From 04f0d792bb2460845d1cf4b917da0edc7295dad5 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 17:36:53 -0700 Subject: [PATCH 0040/1003] Reverted grab back to 5 hz --- 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 9bb293126a..3445547fa1 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -67,7 +67,7 @@ var MSEC_PER_SEC = 1000.0; var startTime = Date.now(); var LIFETIME = 10; var ACTION_LIFETIME = 10; // seconds -var PICKS_PER_SECOND_PER_HAND = 60; +var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; // states for the state machine From 2f620e654bf6c5a2b8c2466d39f6039fec4e3731 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 17:37:51 -0700 Subject: [PATCH 0041/1003] Fixed styling --- libraries/entities/src/EntityTreeElement.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 89e8e2d6d8..b95b0ca673 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -503,14 +503,13 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con forEachEntity([&](EntityItemPointer entity) { if (entityIdsToInclude.size() > 0) { bool entityInWhiteList = false; - //We only want to search whitelist if there is one, otherwise everything except blacklisted items are valid + // We only want to search whitelist if there is one, otherwise everything except blacklisted items are valid for (int i = 0; i < entityIdsToInclude.size(); i++) { if (entityIdsToInclude.at(i) == entity->getID()) { entityInWhiteList = true; } } if (!entityInWhiteList) { - // qDebug() << "entity not found in whitelist!"; return; } } From 47c3df7587618e5d28f2cd45d33d00d3f88a5fe5 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 12 Oct 2015 17:38:52 -0700 Subject: [PATCH 0042/1003] Cleaned up spacing --- libraries/shared/src/RegisteredMetaTypes.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 27921336cf..9ab0eaecb4 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -116,11 +116,9 @@ QVector qVectorQUuidFromScriptValue(const QScriptValue& array) { QUuid 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++) { From 58d3578fb1e53c3f58165cde3637ec360f1de0bb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 12 Oct 2015 17:59:01 -0700 Subject: [PATCH 0043/1003] 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 0044/1003] 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 0045/1003] 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 0046/1003] 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 0047/1003] 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 0048/1003] 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 309cde7f28735d2a23ac7f3943e7f35ea6513ed1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 13 Oct 2015 10:31:36 -0700 Subject: [PATCH 0049/1003] invert solidity of most toybox objects while held --- unpublishedScripts/hiddenEntityReset.js | 41 +- unpublishedScripts/masterReset.js | 1248 ++++++++++++----------- 2 files changed, 686 insertions(+), 603 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 702aaec378..1b3dd07e5e 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -319,6 +319,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -459,6 +462,9 @@ resetMe: { resetMe: true }, + grabbableKey: { + grabbable: false + } }) }; var target = Entities.addEntity(targetProperties); @@ -492,7 +498,15 @@ compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL + script: scriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) }; targets[index] = Entities.addEntity(targetProperties); @@ -553,6 +567,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + grabbable: false } }) }); @@ -588,7 +605,11 @@ userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } + }) }); @@ -856,6 +877,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }; @@ -945,6 +969,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1019,6 +1046,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1058,6 +1088,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1095,6 +1128,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); @@ -1131,6 +1167,9 @@ userData: JSON.stringify({ resetMe: { resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true } }) }); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index a6c3bfdd16..5eaa35372f 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -291,6 +291,9 @@ MasterReset = function() { userData: JSON.stringify({ resetMe: { resetMe: true + }, + grabbable: { + invertSolidWhileHeld: true } }) }); @@ -443,69 +446,78 @@ MasterReset = function() { function testTargetDistanceFromStart() { targets.forEach(function(target, index) { - var currentPosition = Entities.getEntityProperties(target, "position").position; - var originalPosition = originalPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); + var currentPosition = Entities.getEntityProperties(target, "position").position; + var originalPosition = originalPositions[index]; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); - var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); + var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); - lastPositions[index] = currentPosition; + lastPositions[index] = currentPosition; - if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { + if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { - Entities.deleteEntity(target); + Entities.deleteEntity(target); - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: originalPositions[index], - rotation: rotation, - script: scriptURL + var targetProperties = { + name: 'Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: originalPositions[index], + rotation: rotation, + script: scriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + } + }) }; targets[index] = Entities.addEntity(targetProperties); } }); - } - - - function deleteEntity(entityID) { - if (entityID === targetIntervalClearer) { - deleteTargets(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteTargets() { - while (targets.length > 0) { - Entities.deleteEntity(targets.pop()); - } - Entities.deleteEntity(targetIntervalClearer); - } - - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); - - addTargets(); - } - function createCat(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; - var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; - var animationSettings = JSON.stringify({ - running: true, - }); - var cat = Entities.addEntity({ + function deleteEntity(entityID) { + if (entityID === targetIntervalClearer) { + deleteTargets(); + Script.clearInterval(distanceCheckInterval); + Entities.deletingEntity.disconnect(deleteEntity); + } + } + + function deleteTargets() { + while (targets.length > 0) { + Entities.deleteEntity(targets.pop()); + } + Entities.deleteEntity(targetIntervalClearer); + } + + Entities.deletingEntity.connect(deleteEntity); + var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); + + addTargets(); + +} + +function createCat(position) { + + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; + var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; + var animationSettings = JSON.stringify({ + running: true, + }); + var cat = Entities.addEntity({ type: "Model", modelURL: modelURL, name: "cat", @@ -525,18 +537,22 @@ MasterReset = function() { z: 0.90716040134429932 }, userData: JSON.stringify({ - resetMe: { - resetMe: true + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } } }) - }); + }); - } +} - function createFlashlight(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; +function createFlashlight(position) { + var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - var flashlight = Entities.addEntity({ + var flashlight = Entities.addEntity({ type: "Model", modelURL: modelURL, name: "flashlight", @@ -560,345 +576,353 @@ MasterReset = function() { }, shapeType: 'box', userData: JSON.stringify({ - resetMe: { - resetMe: true + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createLights() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; +function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; - var rotation = { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: lightsScriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Hall Light" - } - }) - }); + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: lightsScriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Hall Light" + } + }) + }); - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); - rotation = { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }; - axis = { - x: 0, - y: 1, - z: 0 - }; - dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: lightsScriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Garage Light" - } - }) - }); + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: lightsScriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Garage Light" + } + }) + }); - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); - } +} - function createDice() { - var diceProps = { - type: "Model", - modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", - name: "dice", - position: { - x: 541, - y: 494.96, - z: 509.1 - }, - dimensions: { - x: 0.09, - y: 0.09, - z: 0.09 - }, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: "box", - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }; - var dice1 = Entities.addEntity(diceProps); - - diceProps.position = { - x: 541.05, +function createDice() { + var diceProps = { + type: "Model", + modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", + name: "dice", + position: { + x: 541, y: 494.96, - z: 509.0 - }; - - var dice2 = Entities.addEntity(diceProps); - - } - - - function createGates() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; - - var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); - var gate = Entities.addEntity({ - name: 'Front Door Fence', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'box', - position: { - x: 531.15, - y: 495.11, - z: 520.20 - }, - dimensions: { - x: 1.42, - y: 1.13, - z: 0.2 - }, - rotation: rotation, - collisionsWillMove: true, - gravity: { - x: 0, - y: -100, - z: 0 - }, - linearDamping: 1, - angularDamping: 10, - mass: 10, - userData: JSON.stringify({ + z: 509.1 + }, + dimensions: { + x: 0.09, + y: 0.09, + z: 0.09 + }, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: "box", + collisionsWillMove: true, + userData: JSON.stringify({ resetMe: { resetMe: true, }, grabbableKey: { - grabbable: false + invertSolidWhileHeld: true } - }) - }); - } + } + }) +}; +var dice1 = Entities.addEntity(diceProps); - function createPingPongBallGun() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; +diceProps.position = { + x: 541.05, + y: 494.96, + z: 509.0 +}; - var position = { - x: 548.6, - y: 495.4, - z: 503.39 - }; +var dice2 = Entities.addEntity(diceProps); - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); +} - var pingPongGun = Entities.addEntity({ + +function createGates() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + + var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); + var gate = Entities.addEntity({ + name: 'Front Door Fence', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'box', + position: { + x: 531.15, + y: 495.11, + z: 520.20 + }, + dimensions: { + x: 1.42, + y: 1.13, + z: 0.2 + }, + rotation: rotation, + collisionsWillMove: true, + gravity: { + x: 0, + y: -100, + z: 0 + }, + linearDamping: 1, + angularDamping: 10, + mass: 10, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); +} + +function createPingPongBallGun() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + + var position = { + x: 548.6, + y: 495.4, + z: 503.39 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + + var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, shapeType: 'box', @@ -917,57 +941,61 @@ MasterReset = function() { }, collisionsWillMove: true, userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); - } + }); +} - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); +function createBasketballHoop() { + var position = { + x: 539.23, + y: 496.13, + z: 475.89 + }; + var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; + var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 + var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: position, + rotation: rotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true, }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } + grabbableKey: { + grabbable: false + } + }) + }); +} - function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; +function createWand(position) { + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ name: 'Bubble Wand', type: "Model", modelURL: WAND_MODEL, @@ -991,20 +1019,24 @@ MasterReset = function() { // velocity: {x: 0, y: -0.01, z:0}, script: wandScriptURL, userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createBasketBall(position) { +function createBasketBall(position) { - var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; + var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ type: "Model", modelURL: modelURL, position: position, @@ -1030,24 +1062,28 @@ MasterReset = function() { }, collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createDoll(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; +function createDoll(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; - var naturalDimensions = { - x: 1.63, - y: 1.67, - z: 0.26 - }; - var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - var entity = Entities.addEntity({ + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + var entity = Entities.addEntity({ type: "Model", name: "doll", modelURL: modelURL, @@ -1067,19 +1103,23 @@ MasterReset = function() { }, collisionsWillMove: true, userData: JSON.stringify({ - resetMe: { - resetMe: true, + resetMe: { + resetMe: true, + }, + grabbableKey: { + invertSolidWhileHeld: true + } } }) - }); + }); - } +} - function createSprayCan(position) { +function createSprayCan(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; + var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ type: "Model", name: "spraycan", script: sprayPaintScriptURL, @@ -1103,182 +1143,186 @@ MasterReset = function() { z: 0 }, userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); - - } - - function createPottedPlant(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "Potted Plant", - modelURL: modelURL, - position: position, - dimensions: { - x: 1.10, - y: 2.18, - z: 1.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.4, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - - function createCombinedArmChair(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; - var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; - - var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); - - var entity = Entities.addEntity({ - type: "Model", - name: "Red Arm Chair", - modelURL: modelURL, - shapeType: 'compound', - compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, - position: position, - rotation: rotation, - dimensions: { - x: 1.26, - y: 1.56, - z: 1.35 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -0.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.2, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - - } - - function createBlocks(position) { - var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; - var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; - var NUM_BLOCKS_PER_COLOR = 4; - var i, j; - - var blockTypes = [{ - url: "planky_blue.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_green.fbx", - dimensions: { - x: 0.1, - y: 0.1, - z: 0.25 - } - }, { - url: "planky_natural.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.05 - } - }, { - url: "planky_yellow.fbx", - dimensions: { - x: 0.03, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_red.fbx", - dimensions: { - x: 0.1, - y: 0.05, - z: 0.25 - } - }, ]; - - var modelURL, entity; - for (i = 0; i < blockTypes.length; i++) { - for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { - modelURL = baseURL + blockTypes[i].url; - entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: Vec3.sum(position, { - x: j / 10, - y: i / 10, - z: 0 - }), - shapeType: 'box', - name: "block", - dimensions: blockTypes[i].dimensions, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - gravity: { - x: 0, - y: -2.5, - z: 0 + resetMe: { + resetMe: true, }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); + grabbableKey: { + invertSolidWhileHeld: true + } + } + }) + }); +} + +function createPottedPlant(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "Potted Plant", + modelURL: modelURL, + position: position, + dimensions: { + x: 1.10, + y: 2.18, + z: 1.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.4, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false } + }) + }); +} + + +function createCombinedArmChair(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; + var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; + + var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); + + var entity = Entities.addEntity({ + type: "Model", + name: "Red Arm Chair", + modelURL: modelURL, + shapeType: 'compound', + compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, + position: position, + rotation: rotation, + dimensions: { + x: 1.26, + y: 1.56, + z: 1.35 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -0.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.2, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + }, + grabbableKey: { + grabbable: false + } + }) + }); + +} + +function createBlocks(position) { + var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var NUM_BLOCKS_PER_COLOR = 4; + var i, j; + + var blockTypes = [{ + url: "planky_blue.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_green.fbx", + dimensions: { + x: 0.1, + y: 0.1, + z: 0.25 + } + }, { + url: "planky_natural.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + } + }, { + url: "planky_yellow.fbx", + dimensions: { + x: 0.03, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_red.fbx", + dimensions: { + x: 0.1, + y: 0.05, + z: 0.25 + } + }, ]; + + var modelURL, entity; + for (i = 0; i < blockTypes.length; i++) { + for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { + modelURL = baseURL + blockTypes[i].url; + entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: Vec3.sum(position, { + x: j / 10, + y: i / 10, + z: 0 + }), + shapeType: 'box', + name: "block", + dimensions: blockTypes[i].dimensions, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + gravity: { + x: 0, + y: -2.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + } + }) + }); + } } +} - function cleanup() { - deleteAllToys(); - } +function cleanup() { + deleteAllToys(); +} - if (shouldDeleteOnEndScript) { +if (shouldDeleteOnEndScript) { - Script.scriptEnding.connect(cleanup); - } + Script.scriptEnding.connect(cleanup); +} }; \ No newline at end of file From cf74cfb50e48ae6b5bafeb2e397cb25fda31e3d3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 10:38:52 -0700 Subject: [PATCH 0050/1003] add an ATPAssetMigrator for bulk ATP migration --- interface/src/Menu.cpp | 10 +- interface/src/Menu.h | 1 + interface/src/assets/ATPAssetMigrator.cpp | 177 ++++++++++++++++++ interface/src/assets/ATPAssetMigrator.h | 48 +++++ interface/src/ui/AssetUploadDialogFactory.cpp | 51 +++-- interface/src/ui/AssetUploadDialogFactory.h | 3 +- libraries/networking/src/AssetClient.cpp | 66 ++++--- libraries/networking/src/AssetClient.h | 1 + libraries/networking/src/AssetUpload.cpp | 117 ++++++++---- libraries/networking/src/AssetUpload.h | 7 +- 10 files changed, 385 insertions(+), 96 deletions(-) create mode 100644 interface/src/assets/ATPAssetMigrator.cpp create mode 100644 interface/src/assets/ATPAssetMigrator.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae41ca2493..cc9b259e06 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -24,6 +24,7 @@ #include "Application.h" #include "AccountManager.h" +#include "assets/ATPAssetMigrator.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "devices/DdeFaceTracker.h" @@ -354,7 +355,7 @@ Menu::Menu() { MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets"); auto& assetDialogFactory = AssetUploadDialogFactory::getInstance(); - assetDialogFactory.setParent(this); + assetDialogFactory.setDialogParent(this); QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::UploadAsset, @@ -365,6 +366,13 @@ Menu::Menu() { // disable the asset upload action by default - it gets enabled only if asset server becomes present assetUpload->setEnabled(false); + auto& atpMigrator = ATPAssetMigrator::getInstance(); + atpMigrator.setDialogParent(this); + + QAction* assetMigration = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, + 0, &atpMigrator, + SLOT(loadEntityServerFile())); + MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4c9c3ef7b5..43388fcbd1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -135,6 +135,7 @@ namespace MenuOption { const QString AnimDebugDrawAnimPose = "Debug Draw Animation"; const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose"; const QString Antialiasing = "Antialiasing"; + const QString AssetMigration = "ATP Asset Migration"; const QString Atmosphere = "Atmosphere"; const QString Attachments = "Attachments..."; const QString AudioNoiseReduction = "Audio Noise Reduction"; diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp new file mode 100644 index 0000000000..d32378085b --- /dev/null +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -0,0 +1,177 @@ +// +// ATPAssetMigrator.cpp +// interface/src/assets +// +// Created by Stephen Birarda on 2015-10-12. +// 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 "ATPAssetMigrator.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "../ui/AssetUploadDialogFactory.h" + +ATPAssetMigrator& ATPAssetMigrator::getInstance() { + static ATPAssetMigrator instance; + return instance; +} + +static const QString MODEL_URL_KEY = "modelURL"; + +void ATPAssetMigrator::loadEntityServerFile() { + auto filename = QFileDialog::getOpenFileName(_dialogParent, "Select an entity-server content file to migrate", + QString(), QString("Entity-Server Content (*.gz)")); + + if (!filename.isEmpty()) { + qDebug() << "Selected filename for ATP asset migration: " << filename; + + // try to open the file at the given filename + QFile modelsFile { filename }; + + if (modelsFile.open(QIODevice::ReadOnly)) { + QByteArray compressedJsonData = modelsFile.readAll(); + QByteArray jsonData; + + if (!gunzip(compressedJsonData, jsonData)) { + QMessageBox::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format."); + } + + QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData); + _entitiesArray = modelsJSON.object()["Entities"].toArray(); + + for (auto jsonValue : _entitiesArray) { + QJsonObject entityObject = jsonValue.toObject(); + QString modelURLString = entityObject.value(MODEL_URL_KEY).toString(); + + if (!modelURLString.isEmpty()) { + QUrl modelURL = QUrl(modelURLString); + + if (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS + || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP) { + + QMessageBox messageBox; + messageBox.setWindowTitle("Asset Migration"); + messageBox.setText("Would you like to migrate the following file to the asset server?"); + messageBox.setInformativeText(modelURL.toDisplayString()); + messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Ok); + + if (messageBox.exec() == QMessageBox::Ok) { + // user wants to migrate this asset + + if (_pendingReplacements.contains(modelURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(modelURL, jsonValue); + } else if (_uploadedAssets.contains(modelURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); + jsonValue = entityObject; + } else { + auto request = ResourceManager::createResourceRequest(this, modelURL); + + qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retreive asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + request->send(); + } + + } + } + } + } + + _doneReading = true; + + } else { + QMessageBox::warning(_dialogParent, "Error", + "There was a problem loading that entity-server file for ATP asset migration. Please try again"); + } + } +} + +void ATPAssetMigrator::migrateResource(ResourceRequest* request) { + // use an asset client to upload the asset + auto assetClient = DependencyManager::get(); + + QFileInfo assetInfo { request->getUrl().fileName() }; + + auto upload = assetClient->createUpload(request->getData(), assetInfo.completeSuffix()); + + if (upload) { + // add this URL to our hash of AssetUpload to original URL + _originalURLs.insert(upload, request->getUrl()); + + qDebug() << "Starting upload of asset from" << request->getUrl(); + + // connect to the finished signal so we know when the AssetUpload is done + QObject::connect(upload, &AssetUpload::finished, this, &ATPAssetMigrator::assetUploadFinished); + + // start the upload now + upload->start(); + } else { + // show a QMessageBox to say that there is no local asset server + QString messageBoxText = QString("Could not upload \n\n%1\n\nbecause you are currently not connected" \ + " to a local asset-server.").arg(assetInfo.fileName()); + + QMessageBox::information(_dialogParent, "Failed to Upload", messageBoxText); + } +} + +void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& hash) { + if (upload->getError() == AssetUpload::NoError) { + + const auto& modelURL = _originalURLs[upload]; + + // successfully uploaded asset - make any required replacements found in the pending replacements + auto values = _pendingReplacements.values(modelURL); + + QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + + for (auto value : values) { + // replace the modelURL in this QJsonValueRef with the hash + QJsonObject valueObject = value.toObject(); + valueObject[MODEL_URL_KEY] = atpURL; + value = valueObject; + } + + // pull the replaced models from _pendingReplacements + _pendingReplacements.remove(modelURL); + + // are we out of pending replacements? if so it is time to save the entity-server file + if (_doneReading && _pendingReplacements.empty()) { + // show a dialog to ask the user where they want to save the file + } + } else { + AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); + } +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h new file mode 100644 index 0000000000..84f05b600d --- /dev/null +++ b/interface/src/assets/ATPAssetMigrator.h @@ -0,0 +1,48 @@ +// +// ATPAssetMigrator.h +// interface/src/assets +// +// Created by Stephen Birarda on 2015-10-12. +// 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_ATPAssetMigrator_h +#define hifi_ATPAssetMigrator_h + +#include +#include +#include + +class AssetUpload; +class ResourceRequest; + +class ATPAssetMigrator : public QObject { + Q_OBJECT +public: + static ATPAssetMigrator& getInstance(); + + void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } +public slots: + void loadEntityServerFile(); +private slots: + void assetUploadFinished(AssetUpload* upload, const QString& hash); +private: + void migrateResource(ResourceRequest* request); + + QWidget* _dialogParent = nullptr; + QJsonArray _entitiesArray; + + bool _doneReading { false }; + + QMultiHash _pendingReplacements; + QHash _uploadedAssets; + QHash _originalURLs; +}; + + +#endif // hifi_ATPAssetMigrator_h diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 4910e6d604..7b1611b616 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -29,7 +29,7 @@ AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() { return staticInstance; } -static const QString PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server."; + void AssetUploadDialogFactory::showDialog() { auto nodeList = DependencyManager::get(); @@ -59,7 +59,7 @@ void AssetUploadDialogFactory::showDialog() { } } else { // we don't have permission to upload to asset server in this domain - show the permission denied error - showErrorDialog(QString(), PERMISSION_DENIED_ERROR); + showErrorDialog(nullptr, _dialogParent, AssetUpload::PERMISSION_DENIED_ERROR); } } @@ -117,42 +117,33 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q // show the new dialog hashCopyDialog->show(); } else { - // figure out the right error message for the message box - QString additionalError; - - switch (upload->getError()) { - case AssetUpload::PermissionDenied: - additionalError = PERMISSION_DENIED_ERROR; - break; - case AssetUpload::TooLarge: - additionalError = "The uploaded content was too large and could not be stored in the asset-server."; - break; - case AssetUpload::FileOpenError: - additionalError = "The file could not be opened. Please check your permissions and try again."; - break; - case AssetUpload::NetworkError: - additionalError = "The file could not be opened. Please check your network connectivity."; - break; - default: - // not handled, do not show a message box - return; - } - // display a message box with the error - showErrorDialog(QFileInfo(upload->getFilename()).fileName(), additionalError); + showErrorDialog(upload, _dialogParent); } upload->deleteLater(); } -void AssetUploadDialogFactory::showErrorDialog(const QString& filename, const QString& additionalError) { - QString errorMessage; +void AssetUploadDialogFactory::showErrorDialog(AssetUpload* upload, QWidget* dialogParent, const QString& overrideMessage) { + QString filename; - if (!filename.isEmpty()) { - errorMessage += QString("Failed to upload %1.\n\n").arg(filename); + if (upload) { + filename = QFileInfo { upload->getFilename() }.fileName(); } - errorMessage += additionalError; + QString errorMessage = overrideMessage; - QMessageBox::warning(_dialogParent, "Failed Upload", errorMessage); + if (errorMessage.isEmpty() && upload) { + errorMessage = upload->getErrorString(); + } + + QString dialogMessage; + + if (upload) { + dialogMessage += QString("Failed to upload %1.\n\n").arg(filename); + } + + dialogMessage += errorMessage; + + QMessageBox::warning(dialogParent, "Failed Upload", dialogMessage); } diff --git a/interface/src/ui/AssetUploadDialogFactory.h b/interface/src/ui/AssetUploadDialogFactory.h index a8e8765f20..fe3ecf5dc4 100644 --- a/interface/src/ui/AssetUploadDialogFactory.h +++ b/interface/src/ui/AssetUploadDialogFactory.h @@ -25,6 +25,7 @@ public: AssetUploadDialogFactory& operator=(const AssetUploadDialogFactory& rhs) = delete; static AssetUploadDialogFactory& getInstance(); + static void showErrorDialog(AssetUpload* upload, QWidget* dialogParent, const QString& overrideMessage = QString()); void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; } @@ -35,7 +36,7 @@ public slots: private: AssetUploadDialogFactory() = default; - void showErrorDialog(const QString& filename, const QString& additionalError); + QWidget* _dialogParent { nullptr }; }; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 87af2a5cf8..b7f1205847 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -65,43 +65,65 @@ void AssetClient::init() { } } +bool haveAssetServer() { + auto nodeList = DependencyManager::get(); + SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + + if (!assetServer) { + qCWarning(asset_client) << "Could not complete AssetClient operation " + << "since you are not currently connected to an asset-server."; + return false; + } + + return true; +} + AssetRequest* AssetClient::createRequest(const QString& hash, const QString& extension) { if (hash.length() != SHA256_HASH_HEX_LENGTH) { qCWarning(asset_client) << "Invalid hash size"; return nullptr; } - auto nodeList = DependencyManager::get(); - SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); - - if (!assetServer) { - qCWarning(asset_client).nospace() << "Could not request " << hash << "." << extension - << " since you are not currently connected to an asset-server."; + if (haveAssetServer()) { + auto request = new AssetRequest(hash, extension); + + // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) + request->moveToThread(thread()); + request->setParent(this); + + return request; + } else { return nullptr; } - - auto request = new AssetRequest(hash, extension); - - // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) - request->moveToThread(thread()); - - return request; } + + AssetUpload* AssetClient::createUpload(const QString& filename) { - auto nodeList = DependencyManager::get(); - SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); - if (!assetServer) { - qCWarning(asset_client) << "Could not upload" << filename << "since you are not currently connected to an asset-server."; + if (haveAssetServer()) { + auto upload = new AssetUpload(filename); + + upload->moveToThread(thread()); + upload->setParent(this); + + return upload; + } else { return nullptr; } - - auto upload = new AssetUpload(this, filename); +} - upload->moveToThread(thread()); - - return upload; +AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& extension) { + if (haveAssetServer()) { + auto upload = new AssetUpload(data, extension); + + upload->moveToThread(thread()); + upload->setParent(this); + + return upload; + } else { + return nullptr; + } } bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 9b82e63b58..22933ea30b 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -45,6 +45,7 @@ public: Q_INVOKABLE AssetRequest* createRequest(const QString& hash, const QString& extension); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); + Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data, const QString& extension); private slots: void handleAssetGetInfoReply(QSharedPointer packet, SharedNodePointer senderNode); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 92632a43e5..0518b1e7a1 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -17,59 +17,94 @@ #include "AssetClient.h" #include "NetworkLogging.h" -AssetUpload::AssetUpload(QObject* object, const QString& filename) : +const QString AssetUpload::PERMISSION_DENIED_ERROR = "You do not have permission to upload content to this asset-server."; + +AssetUpload::AssetUpload(const QByteArray& data, const QString& extension) : + _data(data), + _extension(extension) +{ + +} + +AssetUpload::AssetUpload(const QString& filename) : _filename(filename) { } +QString AssetUpload::getErrorString() const { + // figure out the right error message for error + switch (_error) { + case AssetUpload::PermissionDenied: + return PERMISSION_DENIED_ERROR; + case AssetUpload::TooLarge: + return "The uploaded content was too large and could not be stored in the asset-server."; + case AssetUpload::FileOpenError: + return "The file could not be opened. Please check your permissions and try again."; + case AssetUpload::NetworkError: + return "The file could not be opened. Please check your network connectivity."; + default: + // not handled, do not show a message box + return QString(); + } +} + void AssetUpload::start() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "start", Qt::AutoConnection); return; } - // try to open the file at the given filename - QFile file { _filename }; + auto data = _data; - if (file.open(QIODevice::ReadOnly)) { + if (data.isEmpty() && !_filename.isEmpty()) { + // try to open the file at the given filename + QFile file { _filename }; - // file opened, read the data and grab the extension - _extension = QFileInfo(_filename).suffix(); - - auto data = file.readAll(); - - // ask the AssetClient to upload the asset and emit the proper signals from the passed callback - auto assetClient = DependencyManager::get(); - - qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; - - assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ - if (!responseReceived) { - _error = NetworkError; - } else { - switch (error) { - case AssetServerError::NoError: - _error = NoError; - break; - case AssetServerError::AssetTooLarge: - _error = TooLarge; - break; - case AssetServerError::PermissionDenied: - _error = PermissionDenied; - break; - default: - _error = FileOpenError; - break; - } - } - emit finished(this, hash); - }); - } else { - // we couldn't open the file - set the error result - _error = FileOpenError; - - // emit that we are done - emit finished(this, QString()); + if (file.open(QIODevice::ReadOnly)) { + + // file opened, read the data and grab the extension + _extension = QFileInfo(_filename).suffix(); + + data = file.readAll(); + } else { + // we couldn't open the file - set the error result + _error = FileOpenError; + + // emit that we are done + emit finished(this, QString()); + } } + + // ask the AssetClient to upload the asset and emit the proper signals from the passed callback + auto assetClient = DependencyManager::get(); + + if (!_filename.isEmpty()) { + qCDebug(asset_client) << "Attempting to upload" << _filename << "to asset-server."; + } else { + qCDebug(asset_client) << "Attempting to upload file of" << _data.size() << "bytes to asset-server."; + } + + + assetClient->uploadAsset(data, _extension, [this](bool responseReceived, AssetServerError error, const QString& hash){ + if (!responseReceived) { + _error = NetworkError; + } else { + switch (error) { + case AssetServerError::NoError: + _error = NoError; + break; + case AssetServerError::AssetTooLarge: + _error = TooLarge; + break; + case AssetServerError::PermissionDenied: + _error = PermissionDenied; + break; + default: + _error = FileOpenError; + break; + } + } + emit finished(this, hash); + }); } diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index 80faa8a9c1..92f677cbf2 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -35,13 +35,17 @@ public: FileOpenError }; - AssetUpload(QObject* parent, const QString& filename); + static const QString PERMISSION_DENIED_ERROR; + + AssetUpload(const QString& filename); + AssetUpload(const QByteArray& data, const QString& extension); Q_INVOKABLE void start(); const QString& getFilename() const { return _filename; } const QString& getExtension() const { return _extension; } const Error& getError() const { return _error; } + QString getErrorString() const; signals: void finished(AssetUpload* upload, const QString& hash); @@ -49,6 +53,7 @@ signals: private: QString _filename; + QByteArray _data; QString _extension; Error _error; }; From c84d2fc36a29ab8ad3fe5163fb9453329445b6f5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 13 Oct 2015 10:44:43 -0700 Subject: [PATCH 0051/1003] fix target script url --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 1b3dd07e5e..13e5f89914 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -498,7 +498,7 @@ compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL, + script: targetsScriptURL, userData: JSON.stringify({ resetMe: { resetMe: true diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 5eaa35372f..f855caa9a1 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -469,7 +469,7 @@ MasterReset = function() { compoundShapeURL: COLLISION_HULL_URL, position: originalPositions[index], rotation: rotation, - script: scriptURL, + script: targetsScriptURL, userData: JSON.stringify({ resetMe: { resetMe: true From 65976f78917104432ffeaaa1ef0710b91266bb31 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 10:51:57 -0700 Subject: [PATCH 0052/1003] add saving of entity file to ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 33 ++++++++++++++++++++++- interface/src/assets/ATPAssetMigrator.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index d32378085b..6dd1a04434 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -31,6 +31,7 @@ ATPAssetMigrator& ATPAssetMigrator::getInstance() { return instance; } +static const QString ENTITIES_OBJECT_KEY = "Entities"; static const QString MODEL_URL_KEY = "modelURL"; void ATPAssetMigrator::loadEntityServerFile() { @@ -169,9 +170,39 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { - // show a dialog to ask the user where they want to save the file + saveEntityServerFile(); } } else { AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); } } + +void ATPAssetMigrator::saveEntityServerFile() { + // show a dialog to ask the user where they want to save the file + QString saveName = QFileDialog::getSaveFileName(_dialogParent, "Save Migrated Entities File"); + + QFile saveFile { saveName }; + + if (saveFile.open(QIODevice::WriteOnly)) { + QJsonObject rootObject; + rootObject[ENTITIES_OBJECT_KEY] = _entitiesArray; + + QJsonDocument newDocument { rootObject }; + QByteArray jsonDataForFile; + + if (gzip(newDocument.toJson(), jsonDataForFile, -1)) { + + saveFile.write(jsonDataForFile); + saveFile.close(); + + QMessageBox::information(_dialogParent, "Success", + QString("Your new entities file has been saved at %1").arg(saveName)); + } else { + QMessageBox::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file."); + } + + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not open file at %1 to write new entities file to.").arg(saveName)); + } +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h index 84f05b600d..2c96b55ba2 100644 --- a/interface/src/assets/ATPAssetMigrator.h +++ b/interface/src/assets/ATPAssetMigrator.h @@ -33,6 +33,7 @@ private slots: void assetUploadFinished(AssetUpload* upload, const QString& hash); private: void migrateResource(ResourceRequest* request); + void saveEntityServerFile(); QWidget* _dialogParent = nullptr; QJsonArray _entitiesArray; From 470c8f7ef893783bf26b2388715acfa6d31319d4 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 11:31:56 -0700 Subject: [PATCH 0053/1003] Painting on whiteboard with no judder --- .../whiteboard/whiteboardEntityScript.js | 108 +++++++++++++++++- .../painting/whiteboard/whiteboardSpawner.js | 2 +- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 1754bbe6ed..9d62fbae54 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -13,16 +13,21 @@ /*global Whiteboard */ -(function() { + +(function() { + Script.include("../../libraries/utils.js"); var _this; var RIGHT_HAND = 1; var LEFT_HAND = 0; var SPATIAL_CONTROLLERS_PER_PALM = 2; var TIP_CONTROLLER_OFFSET = 1; + var MIN_POINT_DISTANCE = 0.02; + var MAX_POINT_DISTANCE = 0.5; + var MAX_POINTS_PER_LINE = 40; + Whiteboard = function() { _this = this; - print("WAAAAAH"); }; Whiteboard.prototype = { @@ -55,19 +60,112 @@ }; this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); - // Comment above line and uncomment below line to test difference in judder when whitelist is and is not provided - // this.intersection = Entities.findRayIntersection(pickRay, true); + if (this.intersection.intersects) { + this.paint(this.intersection.intersection, this.intersection.surfaceNormal); + } else { + this.painting = false; + } + }, + + paint: function(position, normal) { + if (this.painting === false) { + if (this.oldPosition) { + this.newStroke(this.oldPosition); + } else { + this.newStroke(position); + } + this.painting = true; + } + + + var localPoint = Vec3.subtract(position, this.strokeBasePosition); + //Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on + localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, 0.001 + Math.random() * .001)); //rand avoid z fighting + + var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]); + if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) { + //need a minimum distance to avoid binormal NANs + return; + } + if (this.strokePoints.length > 0 && distance > MAX_POINT_DISTANCE) { + //Prevents drawing lines accross models + this.painting = false; + return; + } + if (this.strokePoints.length === 0) { + localPoint = { + x: 0, + y: 0, + z: 0 + }; + } + + this.strokePoints.push(localPoint); + this.strokeNormals.push(this.normal); + this.strokeWidths.push(this.currentStrokeWidth); + Entities.editEntity(this.currentStroke, { + linePoints: this.strokePoints, + normals: this.strokeNormals, + strokeWidths: this.strokeWidths + }); + if (this.strokePoints.length === MAX_POINTS_PER_LINE) { + this.painting = false; + return; + } + this.oldPosition = position; + }, + + + newStroke: function(position) { + + this.strokeBasePosition = position; + this.currentStroke = Entities.addEntity({ + position: position, + type: "PolyLine", + color: this.strokeColor, + dimensions: { + x: 50, + y: 50, + z: 50 + }, + lifetime: 200 + }); + this.strokePoints = []; + this.strokeNormals = []; + this.strokeWidths = []; + + this.strokes.push(this.currentStroke); + }, releaseGrab: function() { + this.painting = false; + this.oldPosition = null; }, preload: function(entityID) { this.entityID = entityID; - this.position = Entities.getEntityProperties(this.entityID, "position").position; + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + this.position = props.position; + this.rotation = props.rotation; + this.normal = Vec3.multiply(Quat.getFront(this.rotation), -1); + this.painting = false; + this.strokeColor = { + red: 170, + green: 50, + blue: 190 + }; + this.strokes = []; + this.currentStrokeWidth = 0.02; }, + unload: function() { + this.strokes.forEach(function(stroke){ + Entities.deleteEntity(stroke); + }); + } + }; // entity scripts always need to return a newly constructed object of our type diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 058d2408a7..6b7f23aecc 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -26,7 +26,7 @@ var whiteboard = Entities.addEntity({ position: center, rotation: rotation, script: scriptURL, - dimensions: {x: 2, y: 1.5, z: 0.1}, + dimensions: {x: 2, y: 1.5, z: 0.01}, color: {red: 255, green: 255, blue: 255} }); From 7c528497402133e049e16d3ffbd7294f6a5e8b9d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 11:36:58 -0700 Subject: [PATCH 0054/1003] change how hold action works --- interface/src/avatar/Avatar.cpp | 62 +++++++++ interface/src/avatar/Avatar.h | 10 ++ interface/src/avatar/AvatarActionHold.cpp | 156 ++++++++++++++-------- interface/src/avatar/AvatarActionHold.h | 3 +- interface/src/avatar/AvatarManager.cpp | 8 ++ interface/src/avatar/AvatarManager.h | 3 +- interface/src/avatar/MyAvatar.cpp | 64 --------- interface/src/avatar/MyAvatar.h | 9 -- 8 files changed, 188 insertions(+), 127 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index c0636314b5..7796e4d840 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -56,6 +56,7 @@ const float DISPLAYNAME_FADE_TIME = 0.5f; const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME); const float DISPLAYNAME_ALPHA = 1.0f; const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f; +const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) { @@ -1167,3 +1168,64 @@ void Avatar::rebuildSkeletonBody() { DependencyManager::get()->updateAvatarPhysicsShape(getSessionUUID()); } +glm::vec3 Avatar::getLeftPalmPosition() { + glm::vec3 leftHandPosition; + getSkeletonModel().getLeftHandPosition(leftHandPosition); + glm::quat leftRotation; + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); + leftHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftRotation); + 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); + return leftRotation; +} + +glm::vec3 Avatar::getRightPalmPosition() { + glm::vec3 rightHandPosition; + getSkeletonModel().getRightHandPosition(rightHandPosition); + glm::quat rightRotation; + getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); + rightHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightRotation); + 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); + return rightRotation; +} diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 534aefd3cb..9a46a145c2 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -174,6 +174,16 @@ public: void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } AvatarMotionState* getMotionState() { return _motionState; } +public slots: + 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: SkeletonModel _skeletonModel; glm::vec3 _skeletonOffset; diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 1fa50b79fd..ced5c83b08 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -22,7 +22,7 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit _relativePosition(glm::vec3(0.0f)), _relativeRotation(glm::quat()), _hand("right"), - _mine(false) + _holderID(QUuid()) { _type = ACTION_TYPE_HOLD; #if WANT_DEBUG @@ -37,48 +37,46 @@ AvatarActionHold::~AvatarActionHold() { } void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - if (!_mine) { - // if a local script isn't updating this, then we are just getting spring-action data over the wire. - // let the super-class handle it. - ObjectActionSpring::updateActionWorker(deltaTimeStep); - return; - } + QSharedPointer avatarManager = DependencyManager::get(); + AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); + std::shared_ptr holdingAvatar = std::static_pointer_cast(holdingAvatarData); - glm::quat rotation; - glm::vec3 position; - glm::vec3 offset; - bool gotLock = withTryReadLock([&]{ - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = myAvatar->getRightPalmPosition(); - palmRotation = myAvatar->getRightPalmRotation(); - } else { - palmPosition = myAvatar->getLeftPalmPosition(); - palmRotation = myAvatar->getLeftPalmRotation(); + if (holdingAvatar) { + glm::quat rotation; + glm::vec3 position; + glm::vec3 offset; + bool gotLock = withTryReadLock([&]{ + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = holdingAvatar->getRightPalmPosition(); + palmRotation = holdingAvatar->getRightPalmRotation(); + } else { + palmPosition = holdingAvatar->getLeftPalmPosition(); + palmRotation = holdingAvatar->getLeftPalmRotation(); + } + + rotation = palmRotation * _relativeRotation; + offset = rotation * _relativePosition; + position = palmPosition + offset; + }); + + if (gotLock) { + gotLock = withTryWriteLock([&]{ + if (_positionalTarget != position || _rotationalTarget != rotation) { + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } + _positionalTarget = position; + _rotationalTarget = rotation; + } + }); } - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; - }); - - if (gotLock) { - gotLock = withTryWriteLock([&]{ - if (_positionalTarget != position || _rotationalTarget != rotation) { - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - } - _positionalTarget = position; - _rotationalTarget = rotation; - } - }); - } - - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); + if (gotLock) { + ObjectActionSpring::updateActionWorker(deltaTimeStep); + } } } @@ -100,7 +98,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { if (!ok) { relativeRotation = _relativeRotation; } - + ok = true; float timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); @@ -115,35 +113,46 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { hand = _hand; } + ok = true; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto holderID = myAvatar->getSessionUUID(); + QString holderIDString = + EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); + if (ok) { + holderID = QUuid(holderIDString); + } + if (relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale - || hand != _hand) { + || hand != _hand + || holderID != _holderID) { withWriteLock([&] { _relativePosition = relativePosition; _relativeRotation = relativeRotation; const float MIN_TIMESCALE = 0.1f; - _linearTimeScale = glm::min(MIN_TIMESCALE, timeScale); + _linearTimeScale = glm::max(MIN_TIMESCALE, timeScale); _angularTimeScale = _linearTimeScale; _hand = hand; + _holderID = holderID; - _mine = true; _active = true; activateBody(); + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } }); } + return true; } - QVariantMap AvatarActionHold::getArguments() { QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&]{ - if (!_mine) { - arguments = ObjectActionSpring::getArguments(); - return; - } - + arguments["holderID"] = _holderID; arguments["relativePosition"] = glmToQMap(_relativePosition); arguments["relativeRotation"] = glmToQMap(_relativeRotation); arguments["timeScale"] = _linearTimeScale; @@ -152,9 +161,52 @@ QVariantMap AvatarActionHold::getArguments() { return arguments; } +QByteArray AvatarActionHold::serialize() const { + QByteArray serializedActionArguments; + QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); + + dataStream << ACTION_TYPE_SPRING; + dataStream << getID(); + dataStream << AvatarActionHold::holdVersion; + + dataStream << _holderID; + dataStream << _relativePosition; + dataStream << _relativeRotation; + dataStream << _linearTimeScale; + dataStream << _hand; + + dataStream << _expires; + dataStream << _tag; + + return serializedActionArguments; +} void AvatarActionHold::deserialize(QByteArray serializedArguments) { - if (!_mine) { - ObjectActionSpring::deserialize(serializedArguments); + QDataStream dataStream(serializedArguments); + + EntityActionType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != AvatarActionHold::holdVersion) { + return; } + + dataStream >> _holderID; + dataStream >> _relativePosition; + dataStream >> _relativeRotation; + dataStream >> _linearTimeScale; + _angularTimeScale = _linearTimeScale; + dataStream >> _hand; + + dataStream >> _expires; + dataStream >> _tag; + + _active = true; } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 3500b5dfa1..613d2ef3a3 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -27,6 +27,7 @@ public: virtual void updateActionWorker(float deltaTimeStep); + QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); private: @@ -35,7 +36,7 @@ private: glm::vec3 _relativePosition; glm::quat _relativeRotation; QString _hand; - bool _mine = false; + QUuid _holderID; }; #endif // hifi_AvatarActionHold_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 69d76db7de..6e52fa1f85 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -343,3 +343,11 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { } } } + + +AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) { + if (sessionID == _myAvatar->getSessionUUID()) { + return std::static_pointer_cast(_myAvatar); + } + return getAvatarHash()[sessionID]; +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 277e931419..35c18dff0b 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -35,7 +35,8 @@ public: void init(); MyAvatar* getMyAvatar() { return _myAvatar.get(); } - + AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID); + void updateMyAvatar(float deltaTime); void updateOtherAvatars(float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..58d677aeec 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -493,70 +493,6 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } } -const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); - -glm::vec3 MyAvatar::getLeftPalmPosition() { - glm::vec3 leftHandPosition; - getSkeletonModel().getLeftHandPosition(leftHandPosition); - glm::quat leftRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); - leftHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftRotation); - return leftHandPosition; -} - -glm::vec3 MyAvatar::getLeftPalmVelocity() { - const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX); - if (palm != NULL) { - return palm->getVelocity(); - } - return glm::vec3(0.0f); -} - -glm::vec3 MyAvatar::getLeftPalmAngularVelocity() { - const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX); - if (palm != NULL) { - return palm->getRawAngularVelocity(); - } - return glm::vec3(0.0f); -} - -glm::quat MyAvatar::getLeftPalmRotation() { - glm::quat leftRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); - return leftRotation; -} - -glm::vec3 MyAvatar::getRightPalmPosition() { - glm::vec3 rightHandPosition; - getSkeletonModel().getRightHandPosition(rightHandPosition); - glm::quat rightRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); - rightHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightRotation); - return rightHandPosition; -} - -glm::vec3 MyAvatar::getRightPalmVelocity() { - const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX); - if (palm != NULL) { - return palm->getVelocity(); - } - return glm::vec3(0.0f); -} - -glm::vec3 MyAvatar::getRightPalmAngularVelocity() { - const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX); - if (palm != NULL) { - return palm->getRawAngularVelocity(); - } - return glm::vec3(0.0f); -} - -glm::quat MyAvatar::getRightPalmRotation() { - glm::quat rightRotation; - getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); - return rightRotation; -} - void MyAvatar::clearReferential() { changeReferential(NULL); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cca0c4152f..60962ff1a6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -199,15 +199,6 @@ public slots: Q_INVOKABLE void updateMotionBehaviorFromMenu(); - glm::vec3 getLeftPalmPosition(); - glm::vec3 getLeftPalmVelocity(); - glm::vec3 getLeftPalmAngularVelocity(); - glm::quat getLeftPalmRotation(); - glm::vec3 getRightPalmPosition(); - glm::vec3 getRightPalmVelocity(); - glm::vec3 getRightPalmAngularVelocity(); - glm::quat getRightPalmRotation(); - void clearReferential(); bool setModelReferential(const QUuid& id); bool setJointReferential(const QUuid& id, int jointIndex); From 261384b4bb0e1244860926254c7b91bdfcab62f5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 13 Oct 2015 11:47:06 -0700 Subject: [PATCH 0055/1003] 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 83780296de1b2139aa4b07ce902112319bed1644 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 12:12:57 -0700 Subject: [PATCH 0056/1003] don't send physical property updates if there's an action on the object --- libraries/entities/src/EntityItem.cpp | 8 ++++++++ libraries/entities/src/EntityItem.h | 1 + libraries/physics/src/EntityMotionState.cpp | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a8f3adc12e..ad1708c7e7 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1788,3 +1788,11 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const { return result; } + +bool EntityItem::shouldSuppressEdits() const { + QHash::const_iterator i = _objectActions.begin(); + while (i != _objectActions.end()) { + // XXX + return true; + } +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index cddd9df312..59651bfaf0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -424,6 +424,7 @@ public: QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); void setActionDataDirty(bool value) const { _actionDataDirty = value; } + bool shouldSuppressEdits() const; protected: diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 42bf9bd438..5cfdd4125f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -291,6 +291,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } + if (_entity->shouldSuppressEdits()) { + return false; + } + // Else we measure the error between current and extrapolated transform (according to expected behavior // of remote EntitySimulation) and return true if the error is significant. From 93e908a592093671fbd6c1e19c97c2a58a5e54f6 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 13 Oct 2015 12:15:13 -0700 Subject: [PATCH 0057/1003] Don't reload/recenter some avatar data on startup, when we don't necessarilly have all the required data initialized. --- interface/src/Application.cpp | 6 +-- interface/src/Application.h | 2 +- interface/src/PluginContainerProxy.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 62 ++++++++++++++------------ interface/src/avatar/MyAvatar.h | 2 +- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 30014f8906..aad480edde 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1559,7 +1559,7 @@ void Application::keyPressEvent(QKeyEvent* event) { cursor->setIcon(Cursor::Icon::DEFAULT); } } else { - resetSensors(); + resetSensors(true); } break; } @@ -3591,7 +3591,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi renderArgs->_viewport = originalViewport; } -void Application::resetSensors() { +void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); DependencyManager::get()->reset(); @@ -3603,7 +3603,7 @@ void Application::resetSensors() { QPoint windowCenter = mainWindow->geometry().center(); _glWidget->cursor().setPos(currentScreen, windowCenter); - getMyAvatar()->reset(); + getMyAvatar()->reset(andReload); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); } diff --git a/interface/src/Application.h b/interface/src/Application.h index a2125e7e09..dc714ad82a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -274,7 +274,7 @@ public slots: void setRawAvatarUpdateThreading(); void setRawAvatarUpdateThreading(bool isThreaded); - void resetSensors(); + void resetSensors(bool andReload = false); void setActiveFaceTracker(); #ifdef HAVE_IVIEWHMD diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index 19bb6ab8de..079d6d0bea 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -144,7 +144,7 @@ void PluginContainerProxy::unsetFullscreen(const QScreen* avoid) { void PluginContainerProxy::requestReset() { // We could signal qApp to sequence this, but it turns out that requestReset is only used from within the main thread anyway. - qApp->resetSensors(); + qApp->resetSensors(true); } void PluginContainerProxy::showDisplayPluginsTools() { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..f5c3acd386 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -140,16 +140,18 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { return AvatarData::toByteArray(cullSmallChanges, sendAll); } -void MyAvatar::reset() { +void MyAvatar::reset(bool andReload) { // Gather animation mode... // This should be simpler when we have only graph animations always on. bool isRig = _rig->getEnableRig(); // seting rig animation to true, below, will clear the graph animation menu item, so grab it now. bool isGraph = _rig->getEnableAnimGraph() || Menu::getInstance()->isOptionChecked(MenuOption::EnableAnimGraph); // ... and get to sane configuration where other activity won't bother us. - qApp->setRawAvatarUpdateThreading(false); - _rig->disableHands = true; - setEnableRigAnimations(true); + if (andReload) { + qApp->setRawAvatarUpdateThreading(false); + _rig->disableHands = true; + setEnableRigAnimations(true); + } // Reset dynamic state. _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false; @@ -158,32 +160,34 @@ void MyAvatar::reset() { _targetVelocity = glm::vec3(0.0f); setThrust(glm::vec3(0.0f)); - // Get fresh data, in case we're really slow and out of wack. - _hmdSensorMatrix = qApp->getHMDSensorPose(); - _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); - _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); + if (andReload) { + // Get fresh data, in case we're really slow and out of wack. + _hmdSensorMatrix = qApp->getHMDSensorPose(); + _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); + _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); - // Reset body position/orientation under the head. - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. - auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; - glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); - glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); + // Reset body position/orientation under the head. + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; + glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); + glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); - // FIXME: Hack to retain the previous behavior wrt height. - // I'd like to make the body match head height, but that will have to wait for separate PR. - worldBodyPos.y = getPosition().y; + // FIXME: Hack to retain the previous behavior wrt height. + // I'd like to make the body match head height, but that will have to wait for separate PR. + worldBodyPos.y = getPosition().y; - setPosition(worldBodyPos); - setOrientation(worldBodyRot); - // If there is any discrepency between positioning and the head (as there is in initial deriveBodyFromHMDSensor), - // we can make that right by setting _bodySensorMatrix = newBodySensorMatrix. - // However, doing so will make the head want to point to the previous body orientation, as cached above. - //_bodySensorMatrix = newBodySensorMatrix; - //updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes + setPosition(worldBodyPos); + setOrientation(worldBodyRot); + // If there is any discrepency between positioning and the head (as there is in initial deriveBodyFromHMDSensor), + // we can make that right by setting _bodySensorMatrix = newBodySensorMatrix. + // However, doing so will make the head want to point to the previous body orientation, as cached above. + //_bodySensorMatrix = newBodySensorMatrix; + //updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes - _skeletonModel.simulate(0.1f); // non-zero - setEnableRigAnimations(false); - _skeletonModel.simulate(0.1f); + _skeletonModel.simulate(0.1f); // non-zero + setEnableRigAnimations(false); + _skeletonModel.simulate(0.1f); + } if (isRig) { setEnableRigAnimations(true); Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, true); @@ -191,8 +195,10 @@ void MyAvatar::reset() { setEnableAnimGraph(true); Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, true); } - _rig->disableHands = false; - qApp->setRawAvatarUpdateThreading(); + if (andReload) { + _rig->disableHands = false; + qApp->setRawAvatarUpdateThreading(); + } } void MyAvatar::update(float deltaTime) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cca0c4152f..02c9f53082 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -58,7 +58,7 @@ public: AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; } AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; } - void reset(); + void reset(bool andReload = false); void update(float deltaTime); void preRender(RenderArgs* renderArgs); From 54eac099b971fc738a99858c5ca1f1d4125748c3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 12:52:53 -0700 Subject: [PATCH 0058/1003] tweak --- libraries/entities/src/EntityItem.cpp | 2 ++ libraries/physics/src/EntityMotionState.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ad1708c7e7..cb141c930d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1795,4 +1795,6 @@ bool EntityItem::shouldSuppressEdits() const { // XXX return true; } + + return false; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5cfdd4125f..6be9938aff 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -280,6 +280,11 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { } _lastStep = simulationStep; + + if (_entity->shouldSuppressEdits()) { + return false; + } + if (glm::length2(_serverVelocity) > 0.0f) { _serverVelocity += _serverAcceleration * dt; _serverVelocity *= powf(1.0f - _body->getLinearDamping(), dt); @@ -291,10 +296,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } - if (_entity->shouldSuppressEdits()) { - return false; - } - // Else we measure the error between current and extrapolated transform (according to expected behavior // of remote EntitySimulation) and return true if the error is significant. From b490f9e2c1967c9891b8f2f8214455e49718cf8d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 13:33:24 -0700 Subject: [PATCH 0059/1003] Drawing line width based on trigger squeeze --- .../whiteboard/whiteboardEntityScript.js | 18 +++++++++++++++--- .../painting/whiteboard/whiteboardSpawner.js | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 9d62fbae54..6bf7a9fd65 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -25,6 +25,11 @@ var MIN_POINT_DISTANCE = 0.02; var MAX_POINT_DISTANCE = 0.5; var MAX_POINTS_PER_LINE = 40; + var MAX_DISTANCE = 5; + + var TRIGGER_ON_VALUE = 0.3; + var MIN_STROKE_WIDTH = 0.001; + var MAX_STROKE_WIDTH = 0.02; Whiteboard = function() { _this = this; @@ -41,6 +46,9 @@ }, startFarGrabNonColliding: function() { + if (this.painting) { + return; + } if (this.hand === RIGHT_HAND) { this.getHandPosition = MyAvatar.getRightPalmPosition; this.getHandRotation = MyAvatar.getRightPalmRotation; @@ -61,7 +69,12 @@ this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); if (this.intersection.intersects) { - this.paint(this.intersection.intersection, this.intersection.surfaceNormal); + var distance = Vec3.distance(handPosition, this.intersection.intersection); + if (distance < MAX_DISTANCE) { + this.triggerValue = Controller.getActionValue(this.triggerAction); + this.currentStrokeWidth = map(this.triggerValue, TRIGGER_ON_VALUE, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); + this.paint(this.intersection.intersection, this.intersection.surfaceNormal); + } } else { this.painting = false; } @@ -157,11 +170,10 @@ blue: 190 }; this.strokes = []; - this.currentStrokeWidth = 0.02; }, unload: function() { - this.strokes.forEach(function(stroke){ + this.strokes.forEach(function(stroke) { Entities.deleteEntity(stroke); }); } diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 6b7f23aecc..4b7b99dc5b 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -30,6 +30,7 @@ var whiteboard = Entities.addEntity({ color: {red: 255, green: 255, blue: 255} }); + function cleanup() { Entities.deleteEntity(whiteboard); } From 8b72f6e7c61b206901d72d0e2eae920395e0b16a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 13:50:43 -0700 Subject: [PATCH 0060/1003] tweak --- interface/src/avatar/AvatarActionHold.cpp | 3 --- libraries/physics/src/EntityMotionState.cpp | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index ced5c83b08..582c636a7a 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -65,9 +65,6 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { gotLock = withTryWriteLock([&]{ if (_positionalTarget != position || _rotationalTarget != rotation) { auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - } _positionalTarget = position; _rotationalTarget = rotation; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6be9938aff..5cfdd4125f 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -280,11 +280,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { } _lastStep = simulationStep; - - if (_entity->shouldSuppressEdits()) { - return false; - } - if (glm::length2(_serverVelocity) > 0.0f) { _serverVelocity += _serverAcceleration * dt; _serverVelocity *= powf(1.0f - _body->getLinearDamping(), dt); @@ -296,6 +291,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } + if (_entity->shouldSuppressEdits()) { + return false; + } + // Else we measure the error between current and extrapolated transform (according to expected behavior // of remote EntitySimulation) and return true if the error is significant. From fcbb107bc0aaf137a3262aef9bbca0413d1a6541 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 14:13:53 -0700 Subject: [PATCH 0061/1003] tweak --- interface/src/avatar/AvatarActionHold.cpp | 7 ++----- libraries/entities/src/EntityItemProperties.h | 7 ++++++- libraries/physics/src/ObjectActionSpring.cpp | 3 +-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 582c636a7a..fa2e40f19e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -63,11 +63,8 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { if (gotLock) { gotLock = withTryWriteLock([&]{ - if (_positionalTarget != position || _rotationalTarget != rotation) { - auto ownerEntity = _ownerEntity.lock(); - _positionalTarget = position; - _rotationalTarget = rotation; - } + _positionalTarget = position; + _rotationalTarget = rotation; }); } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 22a4a298ef..51fa2eee75 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -370,7 +370,12 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, ActionData, actionData, ""); + + // DEBUG_PROPERTY_IF_CHANGED(debug, properties, ActionData, actionData, ""); + if (properties.actionDataChanged()) { + debug << " " << "actionData" << ":" << properties.getActionData().toHex() << "" << "\n"; + } + DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 7d0bab5143..8b936a96e9 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -38,6 +38,7 @@ ObjectActionSpring::~ObjectActionSpring() { } void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { + // don't risk hanging the thread running the physics simulation auto lockResult = withTryReadLock([&]{ auto ownerEntity = _ownerEntity.lock(); if (!ownerEntity) { @@ -100,9 +101,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { } }); if (!lockResult) { - // don't risk hanging the thread running the physics simulation qDebug() << "ObjectActionSpring::updateActionWorker lock failed"; - return; } } From ad61b89abdaf5a81ba9d5908fc1022458cfbba0f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 14:16:28 -0700 Subject: [PATCH 0062/1003] setting up colors, adding some functions to utils.js --- examples/libraries/utils.js | 18 ++++++ .../painting/whiteboard/whiteboardSpawner.js | 60 +++++++++++++++++-- examples/utilities.js | 60 ------------------- 3 files changed, 74 insertions(+), 64 deletions(-) delete mode 100644 examples/utilities.js diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 3583aad76a..ab86007e4b 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -253,3 +253,21 @@ orientationOf = function(vector) { return Quat.multiply(yaw, pitch); } +randFloat = function(low, high) { + return low + Math.random() * (high - low); +} + + +randInt = function(low, high) { + return Math.floor(randFloat(low, high)); +} + +hexToRgb = function(hex) { + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? { + red: parseInt(result[1], 16), + green: parseInt(result[2], 16), + blue: parseInt(result[3], 16) + } : null; +} + diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 4b7b99dc5b..0702aa0360 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -21,19 +21,71 @@ var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); center.y += 0.4; +var whiteboardDimensions = { + x: 2, + y: 1.5, + z: 0.01 +}; var whiteboard = Entities.addEntity({ type: "Box", position: center, rotation: rotation, script: scriptURL, - dimensions: {x: 2, y: 1.5, z: 0.01}, - color: {red: 255, green: 255, blue: 255} + dimensions: whiteboardDimensions, + color: { + red: 255, + green: 255, + blue: 255 + } }); +//COLORS + +var colors = [ + hexToRgb("#2F8E84"), + hexToRgb("#66CCB3"), + hexToRgb("#A43C37"), + hexToRgb("#491849"), + hexToRgb("#6AB03B"), + hexToRgb("#993369"), + hexToRgb("#9B47C2") +]; + +var direction = Quat.getRight(rotation); +var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); +colorBoxPosition.y += whiteboardDimensions.y / 2; + +var colorBoxes = []; + +var colorSquareDimensions = { + x: (whiteboardDimensions.x / 2) / (colors.length - 1), + y: .1, + z: 0.05 +}; +var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); + +for (var i = 0; i < colors.length; i++) { + var colorBox = Entities.addEntity({ + type: "Box", + position: colorBoxPosition, + dimensions: colorSquareDimensions, + rotation: rotation, + color: colors[i] + }); + colorBoxes.push(colorBox); + + colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); + +} + + function cleanup() { Entities.deleteEntity(whiteboard); -} + colorBoxes.forEach(function(colorBox) { + Entities.deleteEntity(colorBox); + }); + } -Script.scriptEnding.connect(cleanup); \ No newline at end of file + Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/utilities.js b/examples/utilities.js deleted file mode 100644 index 85e27079a8..0000000000 --- a/examples/utilities.js +++ /dev/null @@ -1,60 +0,0 @@ -// utilities.js -// examples -// -// Created by Eric Levin on June 8 -// Copyright 2015 High Fidelity, Inc. -// -// Common utilities -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - - - -map = function(value, min1, max1, min2, max2) { - return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); -} - -hslToRgb = function(hslColor) { - var h = hslColor.hue; - var s = hslColor.sat; - var l = hslColor.light; - var r, g, b; - - if (s == 0) { - r = g = b = l; // achromatic - } else { - var hue2rgb = function hue2rgb(p, q, t) { - if (t < 0) t += 1; - if (t > 1) t -= 1; - if (t < 1 / 6) return p + (q - p) * 6 * t; - if (t < 1 / 2) return q; - if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; - return p; - } - - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1 / 3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1 / 3); - } - - return { - red: Math.round(r * 255), - green: Math.round(g * 255), - blue: Math.round(b * 255) - }; - -} - - - -randFloat = function(low, high) { - return low + Math.random() * (high - low); -} - - -randInt = function(low, high) { - return Math.floor(randFloat(low, high)); -} \ No newline at end of file From 755b9719060483faebaacc0d176f610eb12ad2a3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 14:31:52 -0700 Subject: [PATCH 0063/1003] tweak --- examples/controllers/handControllerGrab.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f4bc7496da..9e7ad15179 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -339,11 +339,12 @@ function MyController(hand, triggerAction) { var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity", "ignoreForCollisions"]); + var now = Date.now(); // add the action and initialize some variables this.currentObjectPosition = grabbedProperties.position; this.currentObjectRotation = grabbedProperties.rotation; - this.currentObjectTime = Date.now(); + this.currentObjectTime = now; this.handPreviousPosition = handControllerPosition; this.handPreviousRotation = handRotation; @@ -359,6 +360,7 @@ function MyController(hand, triggerAction) { if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } + this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); if (this.actionID !== null) { this.setState(STATE_CONTINUE_DISTANCE_HOLDING); @@ -454,9 +456,11 @@ function MyController(hand, triggerAction) { angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, lifetime: ACTION_LIFETIME }); + this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); }; this.nearGrabbing = function() { + var now = Date.now(); if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); @@ -490,6 +494,7 @@ function MyController(hand, triggerAction) { if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } else { + this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); this.setState(STATE_CONTINUE_NEAR_GRABBING); if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); @@ -529,9 +534,13 @@ function MyController(hand, triggerAction) { this.currentObjectTime = now; Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - Entities.updateAction(this.grabbedEntity, this.actionID, { - lifetime: ACTION_LIFETIME - }); + if (this.actionTimeout - now < MSEC_PER_SEC) { + // if less than a second left, refresh the actions lifetime + Entities.updateAction(this.grabbedEntity, this.actionID, { + lifetime: ACTION_LIFETIME + }); + this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); + } }; this.nearGrabbingNonColliding = function() { From 257e3845deb3a651dba582deca112d021312ddc7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 14:45:20 -0700 Subject: [PATCH 0064/1003] get action type correct --- interface/src/avatar/AvatarActionHold.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index fa2e40f19e..a035c9e514 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -159,7 +159,7 @@ QByteArray AvatarActionHold::serialize() const { QByteArray serializedActionArguments; QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); - dataStream << ACTION_TYPE_SPRING; + dataStream << ACTION_TYPE_HOLD; dataStream << getID(); dataStream << AvatarActionHold::holdVersion; From a3f948acf8712c7d87902a5ca2a9b95785b5058b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 15:00:38 -0700 Subject: [PATCH 0065/1003] Adding entity script to each color box --- .../whiteboard/colorSelectorEntityScript.js | 47 +++++++++++++++++++ .../whiteboard/whiteboardEntityScript.js | 4 +- .../painting/whiteboard/whiteboardSpawner.js | 11 +++-- 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 examples/painting/whiteboard/colorSelectorEntityScript.js diff --git a/examples/painting/whiteboard/colorSelectorEntityScript.js b/examples/painting/whiteboard/colorSelectorEntityScript.js new file mode 100644 index 0000000000..13f40446f0 --- /dev/null +++ b/examples/painting/whiteboard/colorSelectorEntityScript.js @@ -0,0 +1,47 @@ +// +// colorSelectorEntityScript.js +// examples/toybox/entityScripts +// +// Created by Eric Levin on 9/21/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 +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ +/*global ColorSelector */ + +(function() { + + var _this; + ColorSelector = function() { + _this = this; + }; + + ColorSelector.prototype = { + + startFarGrabNonColliding: function() { + this.selectColor(); + }, + + clickReleaseOnEntity: function() { + this.selectColor(); + }, + + selectColor: function() { + print("COLOR SELECTED"); + }, + + preload: function(entityID) { + this.entityID = entityID; + var props = Entities.getEntityProperties(this.entityID, ["position, color, userData"]); + this.position = props.position; + this.color = props.color; + this.whiteboard = JSON.parse(this.userData).whiteboard; + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new ColorSelector(); +}); \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 6bf7a9fd65..aae154a35f 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -67,7 +67,7 @@ direction: Quat.getUp(this.getHandRotation()) }; - this.intersection = Entities.findRayIntersection(pickRay, true, [this.entityID]); + this.intersection = Entities.findRayIntersection(pickRay, true, this.whitelist); if (this.intersection.intersects) { var distance = Vec3.distance(handPosition, this.intersection.intersection); if (distance < MAX_DISTANCE) { @@ -170,6 +170,7 @@ blue: 190 }; this.strokes = []; + this.whitelist = [this.entityID]; }, unload: function() { @@ -180,6 +181,7 @@ }; + // entity scripts always need to return a newly constructed object of our type return new Whiteboard(); }); \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 0702aa0360..df97a744ef 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -58,19 +58,24 @@ colorBoxPosition.y += whiteboardDimensions.y / 2; var colorBoxes = []; var colorSquareDimensions = { - x: (whiteboardDimensions.x / 2) / (colors.length - 1), + x: (whiteboardDimensions.x / 2) / (colors.length - 1), y: .1, z: 0.05 }; var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); - +var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { var colorBox = Entities.addEntity({ type: "Box", position: colorBoxPosition, dimensions: colorSquareDimensions, rotation: rotation, - color: colors[i] + color: colors[i], + script: scriptURL, + userData: JSON.stringify({ + colorPalette: true, + whiteboard: whiteboard + }) }); colorBoxes.push(colorBox); From ad96d7692191bd959f7760cb6b740d8f19d26493 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 15:06:11 -0700 Subject: [PATCH 0066/1003] implement comfort mode hack --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/MyAvatar.cpp | 79 ++++++++++++++++++++++--------- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ae41ca2493..3d671b0447 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -461,6 +461,7 @@ Menu::Menu() { 0, false, &ConnexionClient::getInstance(), SLOT(toggleConnexion(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4c9c3ef7b5..216a410603 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -159,6 +159,7 @@ namespace MenuOption { const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString Collisions = "Collisions"; + const QString ComfortMode = "Comfort Mode"; const QString Connexion = "Activate 3D Connexion Devices"; const QString Console = "Console..."; const QString ControlWithSpeech = "Control With Speech"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..55690c98f1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1514,33 +1514,68 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float 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 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; + } + } + + // update body orientation by movement inputs + setOrientation(getOrientation() * + glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); + + } else { + // comfort mode.... + _bodyYawDelta = 0.0f; + + static quint64 lastPulse = 0; + quint64 now = usecTimestampNow(); + quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per 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); - // update body orientation by movement inputs - setOrientation(getOrientation() * - glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); if (qApp->getAvatarUpdater()->isHMDMode()) { glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); From c468cabe7e75cc7322f2fff42c83686e3d18d5cb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 13 Oct 2015 15:12:01 -0700 Subject: [PATCH 0067/1003] Fix crash when atp url is empty --- libraries/networking/src/AssetResourceRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index ecbe80cddb..eba9e45e5c 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -24,7 +24,7 @@ void AssetResourceRequest::doSend() { // Make request to atp auto assetClient = DependencyManager::get(); auto parts = _url.path().split(".", QString::SkipEmptyParts); - auto hash = parts[0]; + auto hash = parts.length() > 0 ? parts[0] : ""; auto extension = parts.length() > 1 ? parts[1] : ""; if (hash.length() != SHA256_HASH_HEX_LENGTH) { From 4fa85441b824774dfae0a1a6d1ea4c68382c4463 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 15:14:34 -0700 Subject: [PATCH 0068/1003] tweak --- interface/src/avatar/AvatarActionHold.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index a035c9e514..e4c63da026 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -110,11 +110,11 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { ok = true; auto myAvatar = DependencyManager::get()->getMyAvatar(); auto holderID = myAvatar->getSessionUUID(); - QString holderIDString = - EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); - if (ok) { - holderID = QUuid(holderIDString); - } + // QString holderIDString = + // EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); + // if (ok) { + // holderID = QUuid(holderIDString); + // } if (relativePosition != _relativePosition || relativeRotation != _relativeRotation From 549043f25533ce5506c70b02db11671ac76cd843 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 15:20:37 -0700 Subject: [PATCH 0069/1003] add an option to bulk upload assets in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 141 +++++++++++++++------- interface/src/assets/ATPAssetMigrator.h | 6 + 2 files changed, 105 insertions(+), 42 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 6dd1a04434..a4325d482f 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -33,6 +33,7 @@ ATPAssetMigrator& ATPAssetMigrator::getInstance() { static const QString ENTITIES_OBJECT_KEY = "Entities"; static const QString MODEL_URL_KEY = "modelURL"; +static const QString MESSAGE_BOX_TITLE = "ATP Asset Migration"; void ATPAssetMigrator::loadEntityServerFile() { auto filename = QFileDialog::getOpenFileName(_dialogParent, "Select an entity-server content file to migrate", @@ -41,6 +42,19 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!filename.isEmpty()) { qDebug() << "Selected filename for ATP asset migration: " << filename; + static const QString MIGRATION_CONFIRMATION_TEXT { + "The ATP Asset Migration process will scan the selected entity-server file, upload discovered resources to the"\ + " current asset-server and then save a new entity-server file with the ATP URLs.\n\nAre you ready to"\ + " continue?\n\nMake sure you are connected to the right domain." + }; + + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button == QMessageBox::No) { + return; + } + // try to open the file at the given filename QFile modelsFile { filename }; @@ -62,50 +76,42 @@ void ATPAssetMigrator::loadEntityServerFile() { if (!modelURLString.isEmpty()) { QUrl modelURL = QUrl(modelURLString); - if (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS - || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP) { + if (!_ignoredUrls.contains(modelURL) + && (modelURL.scheme() == URL_SCHEME_HTTP || modelURL.scheme() == URL_SCHEME_HTTPS + || modelURL.scheme() == URL_SCHEME_FILE || modelURL.scheme() == URL_SCHEME_FTP)) { - QMessageBox messageBox; - messageBox.setWindowTitle("Asset Migration"); - messageBox.setText("Would you like to migrate the following file to the asset server?"); - messageBox.setInformativeText(modelURL.toDisplayString()); - messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - messageBox.setDefaultButton(QMessageBox::Ok); - - if (messageBox.exec() == QMessageBox::Ok) { - // user wants to migrate this asset + if (_pendingReplacements.contains(modelURL)) { + // we already have a request out for this asset, just store the QJsonValueRef + // so we can do the hash replacement when the request comes back + _pendingReplacements.insert(modelURL, jsonValue); + } else if (_uploadedAssets.contains(modelURL)) { + // we already have a hash for this asset + // so just do the replacement immediately + entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); + jsonValue = entityObject; + } else if (wantsToMigrateResource(modelURL)) { + auto request = ResourceManager::createResourceRequest(this, modelURL); - if (_pendingReplacements.contains(modelURL)) { - // we already have a request out for this asset, just store the QJsonValueRef - // so we can do the hash replacement when the request comes back - _pendingReplacements.insert(modelURL, jsonValue); - } else if (_uploadedAssets.contains(modelURL)) { - // we already have a hash for this asset - // so just do the replacement immediately - entityObject[MODEL_URL_KEY] = _uploadedAssets.value(modelURL).toString(); - jsonValue = entityObject; - } else { - auto request = ResourceManager::createResourceRequest(this, modelURL); - - qDebug() << "Requesting" << modelURL << "for ATP asset migration"; - - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); - } else { - QMessageBox::warning(_dialogParent, "Error", - QString("Could not retreive asset at %1").arg(modelURL.toString())); - } - request->deleteLater(); - }); - - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(modelURL, jsonValue); - - request->send(); - } - + qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retreive asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + + request->send(); + } else { + _ignoredUrls.insert(modelURL); } } } @@ -165,16 +171,58 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h value = valueObject; } + // add this URL to our list of uploaded assets + _uploadedAssets.insert(modelURL, atpURL); + // pull the replaced models from _pendingReplacements _pendingReplacements.remove(modelURL); // are we out of pending replacements? if so it is time to save the entity-server file if (_doneReading && _pendingReplacements.empty()) { saveEntityServerFile(); + + // reset after the attempted save, success or fail + reset(); } } else { AssetUploadDialogFactory::showErrorDialog(upload, _dialogParent); } + + upload->deleteLater(); +} + +bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { + static bool hasAskedForCompleteMigration { false }; + static bool wantsCompleteMigration { false }; + + + + if (!hasAskedForCompleteMigration) { + // this is the first resource migration - ask the user if they just want to migrate everything + static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n\n"\ + "Select \"Yes\" to upload all discovered assets to the current asset-server immediately.\n\n"\ + "Select \"No\" to be prompted for each discovered asset." + }; + + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT, + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button == QMessageBox::Yes) { + wantsCompleteMigration = true; + } + + hasAskedForCompleteMigration = true; + } + + if (wantsCompleteMigration) { + return true; + } else { + // present a dialog asking the user if they want to migrate this specific resource + auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, + "Would you like to migrate the following resource?\n" + url.toString(), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + return button == QMessageBox::Yes; + } } void ATPAssetMigrator::saveEntityServerFile() { @@ -206,3 +254,12 @@ void ATPAssetMigrator::saveEntityServerFile() { QString("Could not open file at %1 to write new entities file to.").arg(saveName)); } } + +void ATPAssetMigrator::reset() { + _entitiesArray = QJsonArray(); + _doneReading = false; + _pendingReplacements.clear(); + _uploadedAssets.clear(); + _originalURLs.clear(); + _ignoredUrls.clear(); +} diff --git a/interface/src/assets/ATPAssetMigrator.h b/interface/src/assets/ATPAssetMigrator.h index 2c96b55ba2..454eb1eac1 100644 --- a/interface/src/assets/ATPAssetMigrator.h +++ b/interface/src/assets/ATPAssetMigrator.h @@ -17,6 +17,7 @@ #include #include #include +#include class AssetUpload; class ResourceRequest; @@ -35,6 +36,10 @@ private: void migrateResource(ResourceRequest* request); void saveEntityServerFile(); + void reset(); + + bool wantsToMigrateResource(const QUrl& url); + QWidget* _dialogParent = nullptr; QJsonArray _entitiesArray; @@ -43,6 +48,7 @@ private: QMultiHash _pendingReplacements; QHash _uploadedAssets; QHash _originalURLs; + QSet _ignoredUrls; }; From b54b488b9e262363047c8008a08c80154f2dc5ae Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 15:23:02 -0700 Subject: [PATCH 0070/1003] Adding in color selecting --- .../whiteboard/colorSelectorEntityScript.js | 4 +++ .../painting/whiteboard/whiteboardSpawner.js | 31 ++++++++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/examples/painting/whiteboard/colorSelectorEntityScript.js b/examples/painting/whiteboard/colorSelectorEntityScript.js index 13f40446f0..9a77dae196 100644 --- a/examples/painting/whiteboard/colorSelectorEntityScript.js +++ b/examples/painting/whiteboard/colorSelectorEntityScript.js @@ -30,6 +30,10 @@ selectColor: function() { print("COLOR SELECTED"); + Entities.editEntity(this.whiteboard, { + userData: JSON.stringify({currentColor: this.color}) + }); + Entities.callEntityMethod(this.whiteboard, "changeColor"); }, preload: function(entityID) { diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index df97a744ef..b0a0785c82 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -26,20 +26,6 @@ var whiteboardDimensions = { y: 1.5, z: 0.01 }; -var whiteboard = Entities.addEntity({ - type: "Box", - position: center, - rotation: rotation, - script: scriptURL, - dimensions: whiteboardDimensions, - color: { - red: 255, - green: 255, - blue: 255 - } -}); - -//COLORS var colors = [ hexToRgb("#2F8E84"), @@ -50,6 +36,23 @@ var colors = [ hexToRgb("#993369"), hexToRgb("#9B47C2") ]; +var whiteboard = Entities.addEntity({ + type: "Box", + position: center, + rotation: rotation, + script: scriptURL, + dimensions: whiteboardDimensions, + color: { + red: 255, + green: 255, + blue: 255 + }, + userData: JSON.stringify({ + currentColor: colors[0] + }) +}); + +//COLORS var direction = Quat.getRight(rotation); var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); From 4c877e82047e780c303ff806146b95caa37acaae Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 15:46:10 -0700 Subject: [PATCH 0071/1003] Changing color when grabbing colored boxes --- examples/painting/whiteboard/colorSelectorEntityScript.js | 3 +-- examples/painting/whiteboard/whiteboardEntityScript.js | 5 +++++ examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/painting/whiteboard/colorSelectorEntityScript.js b/examples/painting/whiteboard/colorSelectorEntityScript.js index 9a77dae196..20bfeecdc6 100644 --- a/examples/painting/whiteboard/colorSelectorEntityScript.js +++ b/examples/painting/whiteboard/colorSelectorEntityScript.js @@ -29,7 +29,6 @@ }, selectColor: function() { - print("COLOR SELECTED"); Entities.editEntity(this.whiteboard, { userData: JSON.stringify({currentColor: this.color}) }); @@ -41,7 +40,7 @@ var props = Entities.getEntityProperties(this.entityID, ["position, color, userData"]); this.position = props.position; this.color = props.color; - this.whiteboard = JSON.parse(this.userData).whiteboard; + this.whiteboard = JSON.parse(props.userData).whiteboard; }, }; diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index aae154a35f..9c2fb11ac6 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -157,6 +157,11 @@ }, + changeColor: function(){ + print("CHANGE COLOR"); + this.strokeColor = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData).currentColor; + }, + preload: function(entityID) { this.entityID = entityID; var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index b0a0785c82..3f0849fee3 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -56,7 +56,6 @@ var whiteboard = Entities.addEntity({ var direction = Quat.getRight(rotation); var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); -colorBoxPosition.y += whiteboardDimensions.y / 2; var colorBoxes = []; @@ -65,6 +64,7 @@ var colorSquareDimensions = { y: .1, z: 0.05 }; +colorBoxPosition.y += whiteboardDimensions.y / 2 + colorSquareDimensions.y/2 - 0.01; var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { From 2c8b85bb5cb587ca9004e1aa49d1f623dbd2d5ba Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 16:13:24 -0700 Subject: [PATCH 0072/1003] Displaying overlay laser pointer, user must past threshold to paint --- .../whiteboard/whiteboardEntityScript.js | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 9c2fb11ac6..6e35f5d828 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -27,9 +27,9 @@ var MAX_POINTS_PER_LINE = 40; var MAX_DISTANCE = 5; - var TRIGGER_ON_VALUE = 0.3; - var MIN_STROKE_WIDTH = 0.001; - var MAX_STROKE_WIDTH = 0.02; + var PAINT_TRIGGER_THRESHOLD = 0.6; + var MIN_STROKE_WIDTH = 0.0005; + var MAX_STROKE_WIDTH = 0.03; Whiteboard = function() { _this = this; @@ -72,11 +72,25 @@ var distance = Vec3.distance(handPosition, this.intersection.intersection); if (distance < MAX_DISTANCE) { this.triggerValue = Controller.getActionValue(this.triggerAction); - this.currentStrokeWidth = map(this.triggerValue, TRIGGER_ON_VALUE, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); - this.paint(this.intersection.intersection, this.intersection.surfaceNormal); + this.currentStrokeWidth = map(this.triggerValue, 0, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); + var displayPoint = this.intersection.intersection; + displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.intersection.surfaceNormal, -.01)); + Overlays.editOverlay(this.laserPointer, { + position: displayPoint, + size: { + x: this.currentStrokeWidth, + y: this.currentStrokeWidth + } + }); + print("TRIGGER VALUE " + this.triggerValue); + if (this.triggerValue > PAINT_TRIGGER_THRESHOLD) { + this.paint(this.intersection.intersection, this.intersection.surfaceNormal); + } else { + this.releaseGrab(); + } } } else { - this.painting = false; + this.releaseGrab(); } }, @@ -154,34 +168,38 @@ releaseGrab: function() { this.painting = false; this.oldPosition = null; - }, - changeColor: function(){ + changeColor: function() { print("CHANGE COLOR"); this.strokeColor = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData).currentColor; + Overlays.editOverlay(this.laserPointer, { + color: this.strokeColor + }); }, preload: function(entityID) { this.entityID = entityID; - var props = Entities.getEntityProperties(this.entityID, ["position", "rotation"]); + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation", "userData"]); this.position = props.position; this.rotation = props.rotation; this.normal = Vec3.multiply(Quat.getFront(this.rotation), -1); this.painting = false; - this.strokeColor = { - red: 170, - green: 50, - blue: 190 - }; this.strokes = []; this.whitelist = [this.entityID]; + this.strokeColor = JSON.parse(props.userData).currentColor; + this.laserPointer = Overlays.addOverlay("circle3d", { + color: this.strokeColor, + solid: true, + rotation: this.rotation + }); }, unload: function() { this.strokes.forEach(function(stroke) { Entities.deleteEntity(stroke); }); + Overlays.deleteOverlay(this.laserPointer); } }; From 14503053902da26c91daa4e47206ee4cc2495f95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:13:47 -0700 Subject: [PATCH 0073/1003] fix ATP url scheme constant --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index a4325d482f..e5c31d917c 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -162,7 +162,7 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // successfully uploaded asset - make any required replacements found in the pending replacements auto values = _pendingReplacements.values(modelURL); - QString atpURL = QString("%1:%2.%3").arg(ATP_SCHEME).arg(hash).arg(upload->getExtension()); + QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); for (auto value : values) { // replace the modelURL in this QJsonValueRef with the hash From 2ecb7e8d29251382f3b00254aa24e9acc2674b62 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 16:19:12 -0700 Subject: [PATCH 0074/1003] fix bug that was making action deletes fail --- assignment-client/src/AssignmentAction.cpp | 1 + interface/src/avatar/AvatarActionHold.cpp | 5 ----- libraries/entities/src/EntityItem.cpp | 6 ++++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/AssignmentAction.cpp b/assignment-client/src/AssignmentAction.cpp index 8b5650ee42..107a80cf14 100644 --- a/assignment-client/src/AssignmentAction.cpp +++ b/assignment-client/src/AssignmentAction.cpp @@ -25,6 +25,7 @@ AssignmentAction::~AssignmentAction() { void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const { simulation->removeAction(_id); + simulation->applyActionChanges(); } QByteArray AssignmentAction::serialize() const { diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index e4c63da026..60f1b31625 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -110,11 +110,6 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { ok = true; auto myAvatar = DependencyManager::get()->getMyAvatar(); auto holderID = myAvatar->getSessionUUID(); - // QString holderIDString = - // EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); - // if (ok) { - // holderID = QUuid(holderIDString); - // } if (relativePosition != _relativePosition || relativeRotation != _relativeRotation diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cb141c930d..3cd7055d1d 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1720,9 +1720,11 @@ void EntityItem::setActionData(QByteArray actionData) { void EntityItem::setActionDataInternal(QByteArray actionData) { assertWriteLocked(); + if (_allActionsDataCache != actionData) { + _allActionsDataCache = actionData; + deserializeActionsInternal(); + } checkWaitingToRemove(); - _allActionsDataCache = actionData; - deserializeActionsInternal(); } void EntityItem::serializeActions(bool& success, QByteArray& result) const { From 8a2e23cae4909057bb1bf2c7c9505b704dcaebe6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 16:20:52 -0700 Subject: [PATCH 0075/1003] CR feedback --- interface/src/avatar/MyAvatar.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 55690c98f1..aa579b670f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1530,8 +1530,7 @@ void MyAvatar::updateOrientation(float deltaTime) { blend = 1.0f; } _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; - } - else if (_bodyYawDelta != 0.0f) { + } else if (_bodyYawDelta != 0.0f) { // attenuate body rotation speed const float ROTATION_DECAY_TIMESCALE = 0.05f; float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; @@ -1551,12 +1550,14 @@ void MyAvatar::updateOrientation(float deltaTime) { glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); } else { - // comfort mode.... + // 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 second + quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second float driveLeft = _driveKeys[ROT_LEFT]; float driveRight= _driveKeys[ROT_RIGHT]; From 4402242c3c5c60467449bc2245fcb4bccf0bb684 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 16:27:40 -0700 Subject: [PATCH 0076/1003] allow an action to suppress sending of physics related edits to the entity-server --- interface/src/avatar/AvatarActionHold.h | 2 ++ libraries/entities/src/EntityActionInterface.h | 2 ++ libraries/entities/src/EntityItem.cpp | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 613d2ef3a3..ff8d32891b 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,6 +30,8 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); + virtual bool shouldSuppressEdits() { return true; } + private: static const uint16_t holdVersion; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 57f605c3e3..957071bd42 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -51,6 +51,8 @@ public: bool locallyAddedButNotYetReceived = false; + virtual bool shouldSuppressEdits() { return false; } + protected: virtual glm::vec3 getPosition() = 0; virtual void setPosition(glm::vec3 position) = 0; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3cd7055d1d..c9f950bb61 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1794,8 +1794,9 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const { bool EntityItem::shouldSuppressEdits() const { QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { - // XXX - return true; + if (i.value()->shouldSuppressEdits()) { + return true; + } } return false; From 76bfc6218a8e685acb5a09473aefafeb06ffda91 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:36:06 -0700 Subject: [PATCH 0077/1003] add back block removed in merge --- libraries/networking/src/AssetUpload.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index fdf1c0efc4..67c1049d1a 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -100,6 +100,11 @@ void AssetUpload::start() { break; } } + + if (_error == NoError && hash == hashData(_data).toHex()) { + saveToCache(getUrl(hash, _extension), _data); + } + emit finished(this, hash); }); } From cc9376707744c28511edb506902c9c3bfe3654a2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:38:00 -0700 Subject: [PATCH 0078/1003] remove some extra spaces --- interface/src/assets/ATPAssetMigrator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index e5c31d917c..57c0df16ff 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -195,8 +195,6 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) { static bool hasAskedForCompleteMigration { false }; static bool wantsCompleteMigration { false }; - - if (!hasAskedForCompleteMigration) { // this is the first resource migration - ask the user if they just want to migrate everything static const QString COMPLETE_MIGRATION_TEXT { "Do you want to migrate all assets found in this entity-server file?\n\n"\ From e42bbbc10da86cf2aa5dfdfa43ea3bc7611c6890 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 16:41:05 -0700 Subject: [PATCH 0079/1003] Adding erase board feature to whiteboard --- .../painting/whiteboard/whiteboardSpawner.js | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 3f0849fee3..b9062fdfae 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -60,11 +60,11 @@ var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboard var colorBoxes = []; var colorSquareDimensions = { - x: (whiteboardDimensions.x / 2) / (colors.length - 1), + x: (whiteboardDimensions.x / 2) / (colors.length - 1), y: .1, z: 0.05 }; -colorBoxPosition.y += whiteboardDimensions.y / 2 + colorSquareDimensions.y/2 - 0.01; +colorBoxPosition.y += whiteboardDimensions.y / 2 + colorSquareDimensions.y / 2 - 0.01; var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { @@ -81,19 +81,49 @@ for (var i = 0; i < colors.length; i++) { }) }); colorBoxes.push(colorBox); - colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); - } +var eraseBoxDimensions = { + x: 0.5, + y: 0.1, + z: 0.01 +}; + + +var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x/2)); +eraseBoxPosition.y += 0.3; + +var eraseAllText = Entities.addEntity({ + type: "Text", + position: eraseBoxPosition, + rotation: rotation, + dimensions: eraseBoxDimensions, + backgroundColor: { + red: 0, + green: 60, + blue: 0 + }, + textColor: { + red: 255, + green: 10, + blue: 10 + }, + text: "ERASE BOARD", + lineHeight: 0.07 +}); +print(JSON.stringify(Entities.getEntityProperties(eraseAllText))) + + function cleanup() { Entities.deleteEntity(whiteboard); + Entities.deleteEntity(eraseAllText); colorBoxes.forEach(function(colorBox) { - Entities.deleteEntity(colorBox); - }); - } + Entities.deleteEntity(colorBox); + }); +} - Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); \ No newline at end of file From d22c60204137b7e62bfdeb87cb42c30a74fd7616 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:44:13 -0700 Subject: [PATCH 0080/1003] use categorized logging in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 57c0df16ff..56adc38f9b 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,9 @@ #include "../ui/AssetUploadDialogFactory.h" +Q_DECLARE_LOGGING_CATEGORY(asset_migrator); +Q_LOGGING_CATEGORY(asset_migrator, "hf.asset_migrator"); + ATPAssetMigrator& ATPAssetMigrator::getInstance() { static ATPAssetMigrator instance; return instance; @@ -40,7 +44,7 @@ void ATPAssetMigrator::loadEntityServerFile() { QString(), QString("Entity-Server Content (*.gz)")); if (!filename.isEmpty()) { - qDebug() << "Selected filename for ATP asset migration: " << filename; + qCDebug(asset_migrator) << "Selected filename for ATP asset migration: " << filename; static const QString MIGRATION_CONFIRMATION_TEXT { "The ATP Asset Migration process will scan the selected entity-server file, upload discovered resources to the"\ @@ -92,7 +96,7 @@ void ATPAssetMigrator::loadEntityServerFile() { } else if (wantsToMigrateResource(modelURL)) { auto request = ResourceManager::createResourceRequest(this, modelURL); - qDebug() << "Requesting" << modelURL << "for ATP asset migration"; + qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL // to an ATP one once ready @@ -138,7 +142,7 @@ void ATPAssetMigrator::migrateResource(ResourceRequest* request) { // add this URL to our hash of AssetUpload to original URL _originalURLs.insert(upload, request->getUrl()); - qDebug() << "Starting upload of asset from" << request->getUrl(); + qCDebug(asset_migrator) << "Starting upload of asset from" << request->getUrl(); // connect to the finished signal so we know when the AssetUpload is done QObject::connect(upload, &AssetUpload::finished, this, &ATPAssetMigrator::assetUploadFinished); From 574089530a8c8ba040b708c98b6d1d6614cdedbd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:49:12 -0700 Subject: [PATCH 0081/1003] make sure request is not nullptr before using it --- interface/src/assets/ATPAssetMigrator.cpp | 39 +++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 56adc38f9b..c268b351fa 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -96,24 +96,29 @@ void ATPAssetMigrator::loadEntityServerFile() { } else if (wantsToMigrateResource(modelURL)) { auto request = ResourceManager::createResourceRequest(this, modelURL); - qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; + if (request) { + qCDebug(asset_migrator) << "Requesting" << modelURL << "for ATP asset migration"; + + // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL + // to an ATP one once ready + _pendingReplacements.insert(modelURL, jsonValue); + + connect(request, &ResourceRequest::finished, this, [=]() { + if (request->getResult() == ResourceRequest::Success) { + migrateResource(request); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not retrieve asset at %1").arg(modelURL.toString())); + } + request->deleteLater(); + }); + + request->send(); + } else { + QMessageBox::warning(_dialogParent, "Error", + QString("Could not create request for asset at %1").arg(modelURL.toString())); + } - // add this combination of QUrl and QJsonValueRef to our multi hash so we can change the URL - // to an ATP one once ready - _pendingReplacements.insert(modelURL, jsonValue); - - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - migrateResource(request); - } else { - QMessageBox::warning(_dialogParent, "Error", - QString("Could not retreive asset at %1").arg(modelURL.toString())); - } - request->deleteLater(); - }); - - - request->send(); } else { _ignoredUrls.insert(modelURL); } From 8e21999e440576686d03384ad30bde01d133dcb9 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 13 Oct 2015 16:54:37 -0700 Subject: [PATCH 0082/1003] erasing board --- .../whiteboard/colorSelectorEntityScript.js | 2 +- .../whiteboard/eraseBoardEntityScript.js | 45 +++++++++++++++++++ .../whiteboard/whiteboardEntityScript.js | 13 +++++- .../painting/whiteboard/whiteboardSpawner.js | 12 +++-- 4 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 examples/painting/whiteboard/eraseBoardEntityScript.js diff --git a/examples/painting/whiteboard/colorSelectorEntityScript.js b/examples/painting/whiteboard/colorSelectorEntityScript.js index 20bfeecdc6..8ad003035c 100644 --- a/examples/painting/whiteboard/colorSelectorEntityScript.js +++ b/examples/painting/whiteboard/colorSelectorEntityScript.js @@ -1,6 +1,6 @@ // // colorSelectorEntityScript.js -// examples/toybox/entityScripts +// examples/painting/whiteboard // // Created by Eric Levin on 9/21/15. // Copyright 2015 High Fidelity, Inc. diff --git a/examples/painting/whiteboard/eraseBoardEntityScript.js b/examples/painting/whiteboard/eraseBoardEntityScript.js new file mode 100644 index 0000000000..14679c625b --- /dev/null +++ b/examples/painting/whiteboard/eraseBoardEntityScript.js @@ -0,0 +1,45 @@ +// +// eraseBoardEntityScript.js +// examples/painting/whiteboard +// +// Created by Eric Levin on 9/21/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 +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ +/*global BoardEraser */ + +(function() { + + var _this; + BoardEraser = function() { + _this = this; + }; + + BoardEraser.prototype = { + + startFarGrabNonColliding: function() { + this.eraseBoard(); + }, + + clickReleaseOnEntity: function() { + this.eraseBoard(); + }, + + eraseBoard: function() { + Entities.callEntityMethod(this.whiteboard, "eraseBoard"); + }, + + preload: function(entityID) { + this.entityID = entityID; + var props = Entities.getEntityProperties(this.entityID, ["userData"]); + this.whiteboard = JSON.parse(props.userData).whiteboard; + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new BoardEraser(); +}); \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 6e35f5d828..2550754e2a 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -82,7 +82,6 @@ y: this.currentStrokeWidth } }); - print("TRIGGER VALUE " + this.triggerValue); if (this.triggerValue > PAINT_TRIGGER_THRESHOLD) { this.paint(this.intersection.intersection, this.intersection.surfaceNormal); } else { @@ -149,6 +148,7 @@ this.currentStroke = Entities.addEntity({ position: position, type: "PolyLine", + name: "paintStroke", color: this.strokeColor, dimensions: { x: 50, @@ -171,13 +171,22 @@ }, changeColor: function() { - print("CHANGE COLOR"); this.strokeColor = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData).currentColor; Overlays.editOverlay(this.laserPointer, { color: this.strokeColor }); }, + eraseBoard: function() { + var entities = Entities.findEntities(this.position, 5); + entities.forEach(function(entity) { + var name = Entities.getEntityProperties(entity, "name").name; + if(name === "paintStroke") { + Entities.deleteEntity(entity); + } + }); + }, + preload: function(entityID) { this.entityID = entityID; var props = Entities.getEntityProperties(this.entityID, ["position", "rotation", "userData"]); diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index b9062fdfae..691eec5120 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -1,6 +1,6 @@ // // whiteBoardSpawner.js -// examples/painting +// examples/painting/whiteboard // // Created by Eric Levina on 10/12/15. // Copyright 2015 High Fidelity, Inc. @@ -92,12 +92,13 @@ var eraseBoxDimensions = { }; -var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x/2)); +var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2)); eraseBoxPosition.y += 0.3; - +scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraseAllText = Entities.addEntity({ type: "Text", position: eraseBoxPosition, + script: scriptURL, rotation: rotation, dimensions: eraseBoxDimensions, backgroundColor: { @@ -111,7 +112,10 @@ var eraseAllText = Entities.addEntity({ blue: 10 }, text: "ERASE BOARD", - lineHeight: 0.07 + lineHeight: 0.07, + userData: JSON.stringify({ + whiteboard: whiteboard + }) }); print(JSON.stringify(Entities.getEntityProperties(eraseAllText))) From 644076214edfa8d61bc49eef71f6be065a42fe14 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 16:55:04 -0700 Subject: [PATCH 0083/1003] fix hang --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c9f950bb61..c9c431fd32 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1797,6 +1797,7 @@ bool EntityItem::shouldSuppressEdits() const { if (i.value()->shouldSuppressEdits()) { return true; } + i++; } return false; From 2caa7f6d647ab3c75d078caab670388e47cf755b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 16:56:20 -0700 Subject: [PATCH 0084/1003] change getUrl to getATPUrl, use in ATPAssetMigrator --- interface/src/assets/ATPAssetMigrator.cpp | 3 ++- libraries/networking/src/AssetRequest.cpp | 5 ----- libraries/networking/src/AssetRequest.h | 2 +- libraries/networking/src/AssetUpload.cpp | 2 +- libraries/networking/src/AssetUtils.cpp | 4 ++-- libraries/networking/src/AssetUtils.h | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index c268b351fa..861a1df772 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -171,7 +171,8 @@ void ATPAssetMigrator::assetUploadFinished(AssetUpload *upload, const QString& h // successfully uploaded asset - make any required replacements found in the pending replacements auto values = _pendingReplacements.values(modelURL); - QString atpURL = QString("%1:%2.%3").arg(URL_SCHEME_ATP).arg(hash).arg(upload->getExtension()); + + QString atpURL = getATPUrl(hash, upload->getExtension()).toString(); for (auto value : values) { // replace the modelURL in this QJsonValueRef with the hash diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 121b4cd4fd..7e647e9142 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -127,8 +127,3 @@ void AssetRequest::start() { }); }); } - -QUrl AssetRequest::getUrl() const { - return ::getUrl(_hash, _extension); -} - diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index a5275e718a..3c3459b15d 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -46,7 +46,7 @@ public: const QByteArray& getData() const { return _data; } const State& getState() const { return _state; } const Error& getError() const { return _error; } - QUrl getUrl() const; + QUrl getUrl() const { return ::getATPUrl(_hash, _extension); } signals: void finished(AssetRequest* thisRequest); diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 67c1049d1a..e6f467e717 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -102,7 +102,7 @@ void AssetUpload::start() { } if (_error == NoError && hash == hashData(_data).toHex()) { - saveToCache(getUrl(hash, _extension), _data); + saveToCache(getATPUrl(hash, _extension), _data); } emit finished(this, hash); diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 7311d73525..f37e0af820 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -19,7 +19,7 @@ #include "ResourceManager.h" -QUrl getUrl(const QString& hash, const QString& extension) { +QUrl getATPUrl(const QString& hash, const QString& extension) { if (!extension.isEmpty()) { return QUrl(QString("%1:%2.%3").arg(URL_SCHEME_ATP, hash, extension)); } else { @@ -66,4 +66,4 @@ bool saveToCache(const QUrl& url, const QByteArray& file) { qCWarning(asset_client) << "No disk cache to save assets to."; } return false; -} \ No newline at end of file +} diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index 5fd5c9144d..21b6b3f434 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -32,7 +32,7 @@ enum AssetServerError : uint8_t { PermissionDenied }; -QUrl getUrl(const QString& hash, const QString& extension = QString()); +QUrl getATPUrl(const QString& hash, const QString& extension = QString()); QByteArray hashData(const QByteArray& data); From d84994d8e0a1da6bdef40cdb13951e3281882116 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 13 Oct 2015 17:04:45 -0700 Subject: [PATCH 0085/1003] fix casing for ubuntu include failure --- interface/src/assets/ATPAssetMigrator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/assets/ATPAssetMigrator.cpp b/interface/src/assets/ATPAssetMigrator.cpp index 861a1df772..fadf4ca7ad 100644 --- a/interface/src/assets/ATPAssetMigrator.cpp +++ b/interface/src/assets/ATPAssetMigrator.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include From 74ced250c11ac031bf87af38e539128bb409f89b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 17:18:28 -0700 Subject: [PATCH 0086/1003] don't compress joint rotations --- libraries/avatars/src/AvatarData.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a9ff9541ea..8a93cbbac7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -320,7 +320,11 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { for (int i = 0; i < _jointData.size(); i ++) { const JointData& data = _jointData[ i ]; if (validity & (1 << validityBit)) { - destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); + + // destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); + memcpy(destinationBuffer, &data.rotation, sizeof(glm::quat)); + destinationBuffer += sizeof(glm::quat); + } if (++validityBit == BITS_IN_BYTE) { validityBit = 0; @@ -704,7 +708,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validRotations[i]) { _hasNewJointRotations = true; data.rotationSet = true; - sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); + + // sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); + memcpy(&data.rotation, sourceBuffer, sizeof(glm::quat)); + sourceBuffer += sizeof(glm::quat); } } } // numJoints * 8 bytes From e484a904a2da25dba7506411c0759480a2c4134f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 13 Oct 2015 17:36:00 -0700 Subject: [PATCH 0087/1003] Rotate the avatar to align with the HMD while moving MyAvatar: refactored updateFromHMDSensorMatrix() a bit by splitting it into several methods, because it was getting quite large and becoming hard to follow. * beginStraighteningLean() - can be called when we would like to trigger a re-centering action. * shouldBeginStraighteningLean() - contains some of the logic to decide if we should begin a re-centering action. for now it encapulates the capsule check. * processStraighteningLean() - performs the actual re-centering calculation. New code was added to MyAvatar::updateFromHMDSensorMatrix() to trigger re-centering when the avatar speed rises over a threshold. Secondly the Rig::computeMotionAnimationState() state machine for animGraph added a state change hysteresis of 100ms. This hysteresis should help smooth over two issues. 1) When the delta position is 0, because the physics timestep was not evaluated. 2) During re-centering due to desired motion, the avatar velocity can fluctuate causing undesired animation state fluctuation. --- interface/src/avatar/MyAvatar.cpp | 53 +++++++- interface/src/avatar/MyAvatar.h | 6 + libraries/animation/src/AnimStateMachine.cpp | 2 +- libraries/animation/src/Rig.cpp | 133 +++++++++++++------ libraries/animation/src/Rig.h | 2 + 5 files changed, 145 insertions(+), 51 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 605a81e9d9..bb16ac6e21 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -320,6 +320,7 @@ static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRa // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { + // calc deltaTime auto now = usecTimestampNow(); auto deltaUsecs = now - _lastUpdateFromHMDTime; _lastUpdateFromHMDTime = now; @@ -334,21 +335,61 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); - const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds + // It can be more accurate/smooth to use velocity rather than position, + // but some modes (e.g., hmd standing) update position without updating velocity. + // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 positionDelta = getPosition() - _lastPosition; + glm::vec3 workingVelocity = positionDelta / deltaTime; + _lastPosition = getPosition(); + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + bool isMoving; + if (_lastIsMoving) { + isMoving = glm::length(workingVelocity) >= MOVE_EXIT_SPEED_THRESHOLD; + } else { + isMoving = glm::length(workingVelocity) > MOVE_ENTER_SPEED_THRESHOLD; + } + + bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; + _lastIsMoving = isMoving; + + if (shouldBeginStraighteningLean() || hmdIsAtRest || justStartedMoving) { + beginStraighteningLean(); + } + + processStraighteningLean(deltaTime); +} + +void MyAvatar::beginStraighteningLean() { + // begin homing toward derived body position. + if (!_straighteningLean) { + _straighteningLean = true; + _straighteningLeanAlpha = 0.0f; + } +} + +bool MyAvatar::shouldBeginStraighteningLean() const { // define a vertical capsule const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. + // detect if the derived body position is outside of a capsule around the _bodySensorMatrix auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straighteningLean && (capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS) || hmdIsAtRest)) { + bool isBodyPosOutsideCapsule = capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS); - // begin homing toward derived body position. - _straighteningLean = true; - _straighteningLeanAlpha = 0.0f; + if (isBodyPosOutsideCapsule) { + return true; + } else { + return false; + } +} - } else if (_straighteningLean) { +void MyAvatar::processStraighteningLean(float deltaTime) { + if (_straighteningLean) { + + const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cca0c4152f..3b913eb509 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -271,6 +271,10 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } + void beginStraighteningLean(); + bool shouldBeginStraighteningLean() const; + void processStraighteningLean(float deltaTime); + bool cameraInsideHead() const; // These are made private for MyAvatar so that you will use the "use" methods instead @@ -366,6 +370,8 @@ private: quint64 _lastUpdateFromHMDTime = usecTimestampNow(); AtRestDetector _hmdAtRestDetector; + glm::vec3 _lastPosition; + bool _lastIsMoving = false; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 5304cefe46..c0bb66c2a0 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -93,7 +93,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe const float dt = 0.0f; Triggers triggers; _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); -#if WANT_DEBUGa +#if WANT_DEBUG qCDebug(animation) << "AnimStateMachine::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget; #endif _currentState = desiredState; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 340c09060a..25f28a3f64 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -437,16 +437,6 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos static float t = 0.0f; _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); - // default anim vars to notMoving and notTurning - _animVars.set("isMovingForward", false); - _animVars.set("isMovingBackward", false); - _animVars.set("isMovingLeft", false); - _animVars.set("isMovingRight", false); - _animVars.set("isNotMoving", true); - _animVars.set("isTurningLeft", false); - _animVars.set("isTurningRight", false); - _animVars.set("isNotTurning", true); - const float ANIM_WALK_SPEED = 1.4f; // m/s _animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); @@ -470,47 +460,102 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } if (glm::length(localVel) > moveThresh) { - if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { - if (forwardSpeed > 0.0f) { - // forward - _animVars.set("isMovingForward", true); - _animVars.set("isNotMoving", false); - - } else { - // backward - _animVars.set("isMovingBackward", true); - _animVars.set("isNotMoving", false); - } - } else { - if (lateralSpeed > 0.0f) { - // right - _animVars.set("isMovingRight", true); - _animVars.set("isNotMoving", false); - } else { - // left - _animVars.set("isMovingLeft", true); - _animVars.set("isNotMoving", false); - } + if (_desiredState != RigRole::Move) { + _desiredStateAge = 0.0f; } - _state = RigRole::Move; + _desiredState = RigRole::Move; } else { if (fabsf(turningSpeed) > turnThresh) { - if (turningSpeed > 0.0f) { - // turning right - _animVars.set("isTurningRight", true); - _animVars.set("isNotTurning", false); - } else { - // turning left - _animVars.set("isTurningLeft", true); - _animVars.set("isNotTurning", false); + if (_desiredState != RigRole::Turn) { + _desiredStateAge = 0.0f; } - _state = RigRole::Turn; - } else { - // idle - _state = RigRole::Idle; + _desiredState = RigRole::Turn; + } else { // idle + if (_desiredState != RigRole::Idle) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Idle; } } + const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; + + if ((_desiredStateAge >= STATE_CHANGE_HYSTERESIS_TIMER) && _desiredState != _state) { + _state = _desiredState; + _desiredStateAge = 0.0f; + } + + _desiredStateAge += deltaTime; + + if (_state == RigRole::Move) { + if (glm::length(localVel) > MOVE_ENTER_SPEED_THRESHOLD) { + if (fabsf(forwardSpeed) > 0.5f * fabsf(lateralSpeed)) { + if (forwardSpeed > 0.0f) { + // forward + _animVars.set("isMovingForward", true); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", false); + + } else { + // backward + _animVars.set("isMovingBackward", true); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", false); + } + } else { + if (lateralSpeed > 0.0f) { + // right + _animVars.set("isMovingRight", true); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isNotMoving", false); + } else { + // left + _animVars.set("isMovingLeft", true); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isNotMoving", false); + } + } + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + } + } else if (_state == RigRole::Turn) { + if (turningSpeed > 0.0f) { + // turning right + _animVars.set("isTurningRight", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isNotTurning", false); + } else { + // turning left + _animVars.set("isTurningLeft", true); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", false); + } + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingRight", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isNotMoving", true); + } else { + // default anim vars to notMoving and notTurning + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + } + t += deltaTime; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 6d9f7b4688..71c27e7213 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -236,6 +236,8 @@ public: Move }; RigRole _state = RigRole::Idle; + RigRole _desiredState = RigRole::Idle; + float _desiredStateAge = 0.0f; float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; }; From c4af4c7b101735e1f9e78b9e8a4c004ba5f5607a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 13 Oct 2015 17:45:29 -0700 Subject: [PATCH 0088/1003] basic mono preview for the oculus display plugin --- .../oculus/OculusDisplayPlugin.cpp | 30 +++++++++++++++---- .../oculus/OculusDisplayPlugin.h | 4 ++- .../stereo/StereoDisplayPlugin.cpp | 5 ++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp index 58675eab4d..3e2290f104 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp @@ -11,6 +11,8 @@ #include "OculusHelpers.h" +#include + #if (OVR_MAJOR_VERSION >= 6) // A base class for FBO wrappers that need to use the Oculus C @@ -135,6 +137,19 @@ const QString & OculusDisplayPlugin::getName() const { return NAME; } +static const QString MONO_PREVIEW = "Mono Preview"; +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; + +void OculusDisplayPlugin::activate() { + + CONTAINER->addMenuItem(MENU_PATH(), MONO_PREVIEW, + [this](bool clicked) { + _monoPreview = clicked; + }, true, true); + CONTAINER->removeMenu(FRAMERATE); + OculusBaseDisplayPlugin::activate(); +} + void OculusDisplayPlugin::customizeContext() { WindowOpenGLDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6) @@ -149,7 +164,7 @@ void OculusDisplayPlugin::customizeContext() { #endif enableVsync(false); // Only enable mirroring if we know vsync is disabled - _enableMirror = !isVsyncEnabled(); + _enablePreview = !isVsyncEnabled(); } void OculusDisplayPlugin::deactivate() { @@ -169,10 +184,15 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi // controlling vsync wglSwapIntervalEXT(0); - // screen mirroring - if (_enableMirror) { + // screen preview mirroring + if (_enablePreview) { auto windowSize = toGlm(_window->size()); - Context::Viewport(windowSize.x, windowSize.y); + if (_monoPreview) { + Context::Viewport(windowSize.x * 2, windowSize.y); + Context::Scissor(0, windowSize.y, windowSize.x, windowSize.y); + } else { + Context::Viewport(windowSize.x, windowSize.y); + } glBindTexture(GL_TEXTURE_2D, finalTexture); GLenum err = glGetError(); Q_ASSERT(0 == err); @@ -216,7 +236,7 @@ void OculusDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSi otherwise the swapbuffer delay will interefere with the framerate of the headset */ void OculusDisplayPlugin::finishFrame() { - if (_enableMirror) { + if (_enablePreview) { swapBuffers(); } doneCurrent(); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h index 7db83884cd..c1224ecf3a 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h @@ -14,6 +14,7 @@ using SwapFboPtr = QSharedPointer; class OculusDisplayPlugin : public OculusBaseDisplayPlugin { public: + virtual void activate() override; virtual void deactivate() override; virtual const QString & getName() const override; @@ -25,7 +26,8 @@ protected: private: static const QString NAME; - bool _enableMirror{ false }; + bool _enablePreview { false }; + bool _monoPreview { true }; #if (OVR_MAJOR_VERSION >= 6) SwapFboPtr _sceneFbo; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 2ea79ed2e0..4f7b0a1a78 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -61,6 +61,8 @@ glm::mat4 StereoDisplayPlugin::getProjection(Eye eye, const glm::mat4& baseProje return eyeProjection; } +static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; + std::vector _screenActions; void StereoDisplayPlugin::activate() { auto screens = qApp->screens(); @@ -76,6 +78,9 @@ void StereoDisplayPlugin::activate() { [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _screenActions[i] = action; } + + CONTAINER->removeMenu(FRAMERATE); + CONTAINER->setFullscreen(qApp->primaryScreen()); WindowOpenGLDisplayPlugin::activate(); } From f61a005ebcda1b54e7ddd7045facffb5c441f43c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 13 Oct 2015 17:47:41 -0700 Subject: [PATCH 0089/1003] cleanups --- libraries/avatars/src/AvatarData.cpp | 11 ++--------- libraries/entities/src/EntityItemProperties.h | 3 --- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8a93cbbac7..a9ff9541ea 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -320,11 +320,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) { for (int i = 0; i < _jointData.size(); i ++) { const JointData& data = _jointData[ i ]; if (validity & (1 << validityBit)) { - - // destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); - memcpy(destinationBuffer, &data.rotation, sizeof(glm::quat)); - destinationBuffer += sizeof(glm::quat); - + destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation); } if (++validityBit == BITS_IN_BYTE) { validityBit = 0; @@ -708,10 +704,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validRotations[i]) { _hasNewJointRotations = true; data.rotationSet = true; - - // sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); - memcpy(&data.rotation, sourceBuffer, sizeof(glm::quat)); - sourceBuffer += sizeof(glm::quat); + sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, data.rotation); } } } // numJoints * 8 bytes diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 51fa2eee75..06f90ffd42 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -370,12 +370,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Href, href, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Description, description, ""); - - // DEBUG_PROPERTY_IF_CHANGED(debug, properties, ActionData, actionData, ""); if (properties.actionDataChanged()) { debug << " " << "actionData" << ":" << properties.getActionData().toHex() << "" << "\n"; } - DEBUG_PROPERTY_IF_CHANGED(debug, properties, XTextureURL, xTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, YTextureURL, yTextureURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ZTextureURL, zTextureURL, ""); From 660bf6b3ea7a4048ac9f208c4ab69c8c70838be2 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Oct 2015 14:47:29 +0200 Subject: [PATCH 0090/1003] ScriptEditor: single quotes support and spelling fix --- interface/src/ScriptHighlighting.cpp | 18 +++++++++--------- interface/src/ScriptHighlighting.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/interface/src/ScriptHighlighting.cpp b/interface/src/ScriptHighlighting.cpp index 125ab1e967..1d31f2823b 100644 --- a/interface/src/ScriptHighlighting.cpp +++ b/interface/src/ScriptHighlighting.cpp @@ -16,7 +16,7 @@ ScriptHighlighting::ScriptHighlighting(QTextDocument* parent) : QSyntaxHighlighter(parent) { _keywordRegex = QRegExp("\\b(break|case|catch|continue|debugger|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with)\\b"); - _qoutedTextRegex = QRegExp("\"[^\"]*(\"){0,1}"); + _quotedTextRegex = QRegExp("(\"[^\"]*(\"){0,1}|\'[^\']*(\'){0,1})"); _multiLineCommentBegin = QRegExp("/\\*"); _multiLineCommentEnd = QRegExp("\\*/"); _numberRegex = QRegExp("[0-9]+(\\.[0-9]+){0,1}"); @@ -29,7 +29,7 @@ void ScriptHighlighting::highlightBlock(const QString& text) { this->highlightKeywords(text); this->formatNumbers(text); this->formatTrueFalse(text); - this->formatQoutedText(text); + this->formatQuotedText(text); this->formatComments(text); } @@ -61,14 +61,14 @@ void ScriptHighlighting::formatComments(const QString& text) { int index = _singleLineComment.indexIn(text); while (index >= 0) { int length = _singleLineComment.matchedLength(); - int quoted_index = _qoutedTextRegex.indexIn(text); + int quoted_index = _quotedTextRegex.indexIn(text); bool valid = true; while (quoted_index >= 0 && valid) { - int quoted_length = _qoutedTextRegex.matchedLength(); + int quoted_length = _quotedTextRegex.matchedLength(); if (quoted_index <= index && index <= (quoted_index + quoted_length)) { valid = false; } - quoted_index = _qoutedTextRegex.indexIn(text, quoted_index + quoted_length); + quoted_index = _quotedTextRegex.indexIn(text, quoted_index + quoted_length); } if (valid) { @@ -78,12 +78,12 @@ void ScriptHighlighting::formatComments(const QString& text) { } } -void ScriptHighlighting::formatQoutedText(const QString& text){ - int index = _qoutedTextRegex.indexIn(text); +void ScriptHighlighting::formatQuotedText(const QString& text){ + int index = _quotedTextRegex.indexIn(text); while (index >= 0) { - int length = _qoutedTextRegex.matchedLength(); + int length = _quotedTextRegex.matchedLength(); setFormat(index, length, Qt::red); - index = _qoutedTextRegex.indexIn(text, index + length); + index = _quotedTextRegex.indexIn(text, index + length); } } diff --git a/interface/src/ScriptHighlighting.h b/interface/src/ScriptHighlighting.h index 232d594308..2eb40796e3 100644 --- a/interface/src/ScriptHighlighting.h +++ b/interface/src/ScriptHighlighting.h @@ -29,14 +29,14 @@ protected: void highlightBlock(const QString& text); void highlightKeywords(const QString& text); void formatComments(const QString& text); - void formatQoutedText(const QString& text); + void formatQuotedText(const QString& text); void formatNumbers(const QString& text); void formatTrueFalse(const QString& text); private: QRegExp _alphacharRegex; QRegExp _keywordRegex; - QRegExp _qoutedTextRegex; + QRegExp _quotedTextRegex; QRegExp _multiLineCommentBegin; QRegExp _multiLineCommentEnd; QRegExp _numberRegex; From 742cc0f511fb692a3824fb9669f7288c07e3497f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 06:17:55 -0700 Subject: [PATCH 0091/1003] rename a function --- interface/src/avatar/AvatarActionHold.h | 2 +- libraries/entities/src/EntityActionInterface.h | 2 +- libraries/entities/src/EntityItem.cpp | 4 ++-- libraries/entities/src/EntityItem.h | 2 +- libraries/physics/src/EntityMotionState.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index ff8d32891b..3cc70ced09 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressEdits() { return true; } + virtual bool shouldSuppressLocationEdits() { return true; } private: static const uint16_t holdVersion; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 957071bd42..44c269f336 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -51,7 +51,7 @@ public: bool locallyAddedButNotYetReceived = false; - virtual bool shouldSuppressEdits() { return false; } + virtual bool shouldSuppressLocationEdits() { return false; } protected: virtual glm::vec3 getPosition() = 0; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index c9c431fd32..5b72ac62c1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1791,10 +1791,10 @@ QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const { return result; } -bool EntityItem::shouldSuppressEdits() const { +bool EntityItem::shouldSuppressLocationEdits() const { QHash::const_iterator i = _objectActions.begin(); while (i != _objectActions.end()) { - if (i.value()->shouldSuppressEdits()) { + if (i.value()->shouldSuppressLocationEdits()) { return true; } i++; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 59651bfaf0..cc8fa6c00d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -424,7 +424,7 @@ public: QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); void setActionDataDirty(bool value) const { _actionDataDirty = value; } - bool shouldSuppressEdits() const; + bool shouldSuppressLocationEdits() const; protected: diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 5cfdd4125f..2621347373 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -291,7 +291,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } - if (_entity->shouldSuppressEdits()) { + if (_entity->shouldSuppressLocationEdits()) { return false; } From cb62527bf93a1b32dcce94095a38583f163b4412 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Oct 2015 09:22:30 -0700 Subject: [PATCH 0092/1003] 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 2a9b1c0fc37abc27ebde5142b2cc33e443156c72 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 09:31:29 -0700 Subject: [PATCH 0093/1003] fix refresh of hold action --- examples/controllers/handControllerGrab.js | 24 ++++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9e7ad15179..ee16eadeed 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -443,7 +443,9 @@ function MyController(hand, triggerAction) { this.currentObjectTime = now; // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), Quat.inverse(this.handPreviousRotation)); + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); this.handPreviousRotation = handRotation; this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); @@ -469,7 +471,7 @@ function MyController(hand, triggerAction) { this.lineOff(); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity", "ignoreForCollisions"]); this.activateEntity(this.grabbedEntity, grabbedProperties); @@ -477,18 +479,18 @@ function MyController(hand, triggerAction) { var handPosition = this.getHandPosition(); var objectRotation = grabbedProperties.rotation; - var offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); var currentObjectPosition = grabbedProperties.position; var offset = Vec3.subtract(currentObjectPosition, handPosition); - var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); this.actionID = NULL_ACTION_ID; this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, - relativePosition: offsetPosition, - relativeRotation: offsetRotation, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, lifetime: ACTION_LIFETIME }); if (this.actionID === NULL_ACTION_ID) { @@ -516,10 +518,10 @@ function MyController(hand, triggerAction) { return; } - // Keep track of the fingertip velocity to impart when we release the object - // Note that the idea of using a constant 'tip' velocity regardless of the + // Keep track of the fingertip velocity to impart when we release the object. + // Note that the idea of using a constant 'tip' velocity regardless of the // object's actual held offset is an idea intended to make it easier to throw things: - // Because we might catch something or transfer it between hands without a good idea + // Because we might catch something or transfer it between hands without a good idea // of it's actual offset, let's try imparting a velocity which is at a fixed radius // from the palm. @@ -537,6 +539,10 @@ function MyController(hand, triggerAction) { if (this.actionTimeout - now < MSEC_PER_SEC) { // if less than a second left, refresh the actions lifetime Entities.updateAction(this.grabbedEntity, this.actionID, { + hand: this.hand === RIGHT_HAND ? "right" : "left", + timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, + relativePosition: this.offsetPosition, + relativeRotation: this.offsetRotation, lifetime: ACTION_LIFETIME }); this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); From 77bb11675d1c1ad7b257b6a34f2d761f994dcc5f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 09:31:44 -0700 Subject: [PATCH 0094/1003] terse entity-edit logging --- .../entities/src/AnimationPropertyGroup.cpp | 13 + .../entities/src/AnimationPropertyGroup.h | 1 + .../entities/src/AtmospherePropertyGroup.cpp | 24 ++ .../entities/src/AtmospherePropertyGroup.h | 3 +- .../entities/src/EntityItemProperties.cpp | 251 ++++++++++++++++++ libraries/entities/src/EntityItemProperties.h | 2 + libraries/entities/src/EntityTree.cpp | 7 +- libraries/entities/src/PropertyGroup.h | 1 + .../entities/src/SkyboxPropertyGroup.cpp | 9 + libraries/entities/src/SkyboxPropertyGroup.h | 1 + libraries/entities/src/StagePropertyGroup.cpp | 21 ++ libraries/entities/src/StagePropertyGroup.h | 1 + 12 files changed, 331 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index d1da465079..f9d8c07443 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -163,6 +163,19 @@ void AnimationPropertyGroup::debugDump() const { qDebug() << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); } +void AnimationPropertyGroup::listChangedProperties(QList& out) { + if (urlChanged()) { + out << "animation-url"; + } + if (fpsChanged()) { + out << "animation-fps"; + } + if (currentFrameChanged()) { + out << "animation-currentFrame"; + } +} + + bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, diff --git a/libraries/entities/src/AnimationPropertyGroup.h b/libraries/entities/src/AnimationPropertyGroup.h index d0d218f4b3..8c40b0c036 100644 --- a/libraries/entities/src/AnimationPropertyGroup.h +++ b/libraries/entities/src/AnimationPropertyGroup.h @@ -37,6 +37,7 @@ public: virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; + virtual void listChangedProperties(QList& out); virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, diff --git a/libraries/entities/src/AtmospherePropertyGroup.cpp b/libraries/entities/src/AtmospherePropertyGroup.cpp index 090fdb6c6f..8e7a5a5262 100644 --- a/libraries/entities/src/AtmospherePropertyGroup.cpp +++ b/libraries/entities/src/AtmospherePropertyGroup.cpp @@ -54,6 +54,30 @@ void AtmospherePropertyGroup::debugDump() const { qDebug() << " Has Stars:" << getHasStars() << " has changed:" << hasStarsChanged(); } +void AtmospherePropertyGroup::listChangedProperties(QList& out) { + if (centerChanged()) { + out << "center"; + } + if (innerRadiusChanged()) { + out << "innerRadius"; + } + if (outerRadiusChanged()) { + out << "outerRadius"; + } + if (mieScatteringChanged()) { + out << "mieScattering"; + } + if (rayleighScatteringChanged()) { + out << "rayleighScattering"; + } + if (scatteringWavelengthsChanged()) { + out << "scatteringWavelengths"; + } + if (hasStarsChanged()) { + out << "hasStars"; + } +} + bool AtmospherePropertyGroup::appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, diff --git a/libraries/entities/src/AtmospherePropertyGroup.h b/libraries/entities/src/AtmospherePropertyGroup.h index 661d91087b..1d43c848b8 100644 --- a/libraries/entities/src/AtmospherePropertyGroup.h +++ b/libraries/entities/src/AtmospherePropertyGroup.h @@ -53,6 +53,7 @@ public: virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; + virtual void listChangedProperties(QList& out); virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, @@ -87,7 +88,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged); - + static const glm::vec3 DEFAULT_CENTER; static const float DEFAULT_INNER_RADIUS; static const float DEFAULT_OUTER_RADIUS; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4cbf7a043c..19c8f779b4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -1519,3 +1519,254 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) { _simulationOwnerChanged = true; } } + +QList EntityItemProperties::listChangedProperties() { + QList out; + if (containsPositionChange()) { + out += "posistion"; + } + if (dimensionsChanged()) { + out += "dimensions"; + } + if (velocityChanged()) { + out += "velocity"; + } + if (nameChanged()) { + out += "name"; + } + if (visibleChanged()) { + out += "visible"; + } + if (rotationChanged()) { + out += "rotation"; + } + if (densityChanged()) { + out += "density"; + } + if (gravityChanged()) { + out += "gravity"; + } + if (accelerationChanged()) { + out += "acceleration"; + } + if (dampingChanged()) { + out += "damping"; + } + if (restitutionChanged()) { + out += "restitution"; + } + if (frictionChanged()) { + out += "friction"; + } + if (lifetimeChanged()) { + out += "lifetime"; + } + if (scriptChanged()) { + out += "script"; + } + if (scriptTimestampChanged()) { + out += "scriptTimestamp"; + } + if (collisionSoundURLChanged()) { + out += "collisionSoundURL"; + } + if (colorChanged()) { + out += "color"; + } + if (colorSpreadChanged()) { + out += "colorSpread"; + } + if (colorStartChanged()) { + out += "colorStart"; + } + if (colorFinishChanged()) { + out += "colorFinish"; + } + if (alphaChanged()) { + out += "alpha"; + } + if (alphaSpreadChanged()) { + out += "alphaSpread"; + } + if (alphaStartChanged()) { + out += "alphaStart"; + } + if (alphaFinishChanged()) { + out += "alphaFinish"; + } + if (modelURLChanged()) { + out += "modelURL"; + } + if (compoundShapeURLChanged()) { + out += "compoundShapeURL"; + } + if (registrationPointChanged()) { + out += "registrationPoint"; + } + if (angularVelocityChanged()) { + out += "angularVelocity"; + } + if (angularDampingChanged()) { + out += "angularDamping"; + } + if (ignoreForCollisionsChanged()) { + out += "ignoreForCollisions"; + } + if (collisionsWillMoveChanged()) { + out += "collisionsWillMove"; + } + if (isSpotlightChanged()) { + out += "isSpotlight"; + } + if (intensityChanged()) { + out += "intensity"; + } + if (exponentChanged()) { + out += "exponent"; + } + if (cutoffChanged()) { + out += "cutoff"; + } + if (lockedChanged()) { + out += "locked"; + } + if (texturesChanged()) { + out += "textures"; + } + if (userDataChanged()) { + out += "userData"; + } + if (simulationOwnerChanged()) { + out += "simulationOwner"; + } + if (textChanged()) { + out += "text"; + } + if (lineHeightChanged()) { + out += "lineHeight"; + } + if (textColorChanged()) { + out += "textColor"; + } + if (backgroundColorChanged()) { + out += "backgroundColor"; + } + if (shapeTypeChanged()) { + out += "shapeType"; + } + if (maxParticlesChanged()) { + out += "maxParticles"; + } + if (lifespanChanged()) { + out += "lifespan"; + } + if (isEmittingChanged()) { + out += "isEmitting"; + } + if (emitRateChanged()) { + out += "emitRate"; + } + if (emitSpeedChanged()) { + out += "emitSpeed"; + } + if (speedSpreadChanged()) { + out += "speedSpread"; + } + if (emitOrientationChanged()) { + out += "emitOrientation"; + } + if (emitDimensionsChanged()) { + out += "emitDimensions"; + } + if (emitRadiusStartChanged()) { + out += "emitRadiusStart"; + } + if (polarStartChanged()) { + out += "polarStart"; + } + if (polarFinishChanged()) { + out += "polarFinish"; + } + if (azimuthStartChanged()) { + out += "azimuthStart"; + } + if (azimuthFinishChanged()) { + out += "azimuthFinish"; + } + if (emitAccelerationChanged()) { + out += "emitAcceleration"; + } + if (accelerationSpreadChanged()) { + out += "accelerationSpread"; + } + if (particleRadiusChanged()) { + out += "particleRadius"; + } + if (radiusSpreadChanged()) { + out += "radiusSpread"; + } + if (radiusStartChanged()) { + out += "radiusStart"; + } + if (radiusFinishChanged()) { + out += "radiusFinish"; + } + if (marketplaceIDChanged()) { + out += "marketplaceID"; + } + if (backgroundModeChanged()) { + out += "backgroundMode"; + } + if (voxelVolumeSizeChanged()) { + out += "voxelVolumeSize"; + } + if (voxelDataChanged()) { + out += "voxelData"; + } + if (voxelSurfaceStyleChanged()) { + out += "voxelSurfaceStyle"; + } + if (hrefChanged()) { + out += "href"; + } + if (descriptionChanged()) { + out += "description"; + } + if (actionDataChanged()) { + out += "actionData"; + } + if (xTextureURLChanged()) { + out += "xTextureURL"; + } + if (yTextureURLChanged()) { + out += "yTextureURL"; + } + if (zTextureURLChanged()) { + out += "zTextureURL"; + } + if (xNNeighborIDChanged()) { + out += "xNNeighborID"; + } + if (yNNeighborIDChanged()) { + out += "yNNeighborID"; + } + if (zNNeighborIDChanged()) { + out += "zNNeighborID"; + } + if (xPNeighborIDChanged()) { + out += "xPNeighborID"; + } + if (yPNeighborIDChanged()) { + out += "yPNeighborID"; + } + if (zPNeighborIDChanged()) { + out += "zPNeighborID"; + } + + getAnimation().listChangedProperties(out); + getAtmosphere().listChangedProperties(out); + getSkybox().listChangedProperties(out); + getStage().listChangedProperties(out); + + return out; +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 06f90ffd42..4ed7454c17 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -253,6 +253,8 @@ public: void setActionDataDirty() { _actionDataChanged = true; } + QList listChangedProperties(); + private: QUuid _id; bool _idSet; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c4c02d364f..b3e639747f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -610,8 +610,11 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi // if the EntityItem exists, then update it startLogging = usecTimestampNow(); if (wantEditLogging()) { - qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; - qCDebug(entities) << " properties:" << properties; + + // qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; + // qCDebug(entities) << " properties:" << properties; + + qCDebug(entities) << "edit" << entityItemID.toString() << properties.listChangedProperties(); } endLogging = usecTimestampNow(); diff --git a/libraries/entities/src/PropertyGroup.h b/libraries/entities/src/PropertyGroup.h index 27d35fa714..138ee020c1 100644 --- a/libraries/entities/src/PropertyGroup.h +++ b/libraries/entities/src/PropertyGroup.h @@ -57,6 +57,7 @@ public: virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const = 0; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) = 0; virtual void debugDump() const { } + virtual void listChangedProperties(QList& out) { } virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index d291627d1e..0fd00bfe6e 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -33,6 +33,15 @@ void SkyboxPropertyGroup::debugDump() const { qDebug() << " URL:" << getURL() << " has changed:" << urlChanged(); } +void SkyboxPropertyGroup::listChangedProperties(QList& out) { + if (colorChanged()) { + out << "skybox-color"; + } + if (urlChanged()) { + out << "skybox-url"; + } +} + bool SkyboxPropertyGroup::appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index 2ebfcfda14..745f762821 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -33,6 +33,7 @@ public: virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; + virtual void listChangedProperties(QList& out); virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, diff --git a/libraries/entities/src/StagePropertyGroup.cpp b/libraries/entities/src/StagePropertyGroup.cpp index 629f65b72f..0c10795d6f 100644 --- a/libraries/entities/src/StagePropertyGroup.cpp +++ b/libraries/entities/src/StagePropertyGroup.cpp @@ -66,6 +66,27 @@ void StagePropertyGroup::debugDump() const { qDebug() << " _automaticHourDay:" << _automaticHourDay; } +void StagePropertyGroup::listChangedProperties(QList& out) { + if (sunModelEnabledChanged()) { + out << "stage-sunModelEnabled"; + } + if (latitudeChanged()) { + out << "stage-latitude"; + } + if (altitudeChanged()) { + out << "stage-altitude"; + } + if (dayChanged()) { + out << "stage-day"; + } + if (hourChanged()) { + out << "stage-hour"; + } + if (automaticHourDayChanged()) { + out << "stage-automaticHourDay"; + } +} + bool StagePropertyGroup::appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, diff --git a/libraries/entities/src/StagePropertyGroup.h b/libraries/entities/src/StagePropertyGroup.h index 95964f8264..c25a1e629e 100644 --- a/libraries/entities/src/StagePropertyGroup.h +++ b/libraries/entities/src/StagePropertyGroup.h @@ -33,6 +33,7 @@ public: virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); virtual void debugDump() const; + virtual void listChangedProperties(QList& out); virtual bool appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, From 9a9e5b962e6437c7e2d85b12b8af305e7d5886f4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 09:45:35 -0700 Subject: [PATCH 0095/1003] make edit logging terse vs not a domain-server setting --- assignment-client/src/entities/EntityServer.cpp | 4 ++++ domain-server/resources/describe-settings.json | 8 ++++++++ libraries/entities/src/EntityTree.cpp | 15 +++++++++------ libraries/entities/src/EntityTree.h | 4 ++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index bdd5728246..f2a4c2664a 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -147,9 +147,13 @@ bool EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio readOptionBool(QString("wantEditLogging"), settingsSectionObject, wantEditLogging); qDebug("wantEditLogging=%s", debug::valueOf(wantEditLogging)); + bool wantTerseEditLogging = false; + readOptionBool(QString("wantTerseEditLogging"), settingsSectionObject, wantTerseEditLogging); + qDebug("wantTerseEditLogging=%s", debug::valueOf(wantTerseEditLogging)); EntityTreePointer tree = std::static_pointer_cast(_tree); tree->setWantEditLogging(wantEditLogging); + tree->setWantTerseEditLogging(wantTerseEditLogging); return true; } diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index eb1c478489..e0038117f0 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -484,6 +484,14 @@ "default": false, "advanced": true }, + { + "name": "wantTerseEditLogging", + "type": "checkbox", + "label": "Edit Logging (Terse)", + "help": "Logging of all edits to entities", + "default": false, + "advanced": true + }, { "name": "verboseDebug", "type": "checkbox", diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b3e639747f..fa2153f059 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -610,10 +610,10 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi // if the EntityItem exists, then update it startLogging = usecTimestampNow(); if (wantEditLogging()) { - - // qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; - // qCDebug(entities) << " properties:" << properties; - + qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID; + qCDebug(entities) << " properties:" << properties; + } + if (wantTerseEditLogging()) { qCDebug(entities) << "edit" << entityItemID.toString() << properties.listChangedProperties(); } endLogging = usecTimestampNow(); @@ -641,6 +641,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi << newEntity->getEntityItemID(); qCDebug(entities) << " properties:" << properties; } + if (wantTerseEditLogging()) { + qCDebug(entities) << "add" << entityItemID.toString() << properties.listChangedProperties(); + } endLogging = usecTimestampNow(); } @@ -872,7 +875,7 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; - if (wantEditLogging()) { + if (wantEditLogging() || wantTerseEditLogging()) { qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; } @@ -916,7 +919,7 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; - if (wantEditLogging()) { + if (wantEditLogging() || wantTerseEditLogging()) { qCDebug(entities) << "User [" << sourceNode->getUUID() << "] deleting entity. ID:" << entityItemID; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index dd1ee01969..ff84a79088 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -175,6 +175,9 @@ public: bool wantEditLogging() const { return _wantEditLogging; } void setWantEditLogging(bool value) { _wantEditLogging = value; } + bool wantTerseEditLogging() const { return _wantTerseEditLogging; } + void setWantTerseEditLogging(bool value) { _wantTerseEditLogging = value; } + bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues); bool readFromMap(QVariantMap& entityDescription); @@ -240,6 +243,7 @@ private: EntitySimulation* _simulation; bool _wantEditLogging = false; + bool _wantTerseEditLogging = false; void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL); From caacbcbe789db119829cd5998c202b66da8fb2f2 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 10:40:55 -0700 Subject: [PATCH 0096/1003] Adjust erase box position --- examples/painting/whiteboard/whiteboardEntityScript.js | 5 ++++- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 2550754e2a..fe47ec1810 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -58,6 +58,7 @@ this.getHandRotation = MyAvatar.getLeftPalmRotation; this.triggerAction = Controller.findAction("LEFT_HAND_CLICK"); } + Overlays.editOverlay(this.laserPointer, {visible: true}); }, continueFarGrabbingNonColliding: function() { @@ -85,7 +86,8 @@ if (this.triggerValue > PAINT_TRIGGER_THRESHOLD) { this.paint(this.intersection.intersection, this.intersection.surfaceNormal); } else { - this.releaseGrab(); + this.painting = false; + this.oldPosition = null; } } } else { @@ -167,6 +169,7 @@ releaseGrab: function() { this.painting = false; + Overlays.editOverlay(this.laserPointer, {visible: false}); this.oldPosition = null; }, diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 691eec5120..32e049aef6 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -92,7 +92,7 @@ var eraseBoxDimensions = { }; -var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2)); +var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01)); eraseBoxPosition.y += 0.3; scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraseAllText = Entities.addEntity({ From 1302b6a2388d466064363067ec428166a1488670 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Oct 2015 10:47:49 -0700 Subject: [PATCH 0097/1003] 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 20d1a2440c9487defcf5c34180976aa457c638db Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:15:49 -0700 Subject: [PATCH 0098/1003] Ignoring unknown types in collision --- examples/controllers/handControllerGrab.js | 3 +- .../whiteboard/whiteboardEntityScript.js | 38 ++++++++++++------- .../painting/whiteboard/whiteboardSpawner.js | 24 ++++++++++-- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 3445547fa1..0f59e80948 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -67,7 +67,7 @@ var MSEC_PER_SEC = 1000.0; var startTime = Date.now(); var LIFETIME = 10; var ACTION_LIFETIME = 10; // seconds -var PICKS_PER_SECOND_PER_HAND = 5; +var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; // states for the state machine @@ -177,6 +177,7 @@ function MyController(hand, triggerAction) { this.continueFarGrabbingNonColliding(); break; case STATE_RELEASE: + print("TRIGGER VALUE " + this.triggerValue); this.release(); break; } diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index fe47ec1810..59977fe52a 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -20,8 +20,6 @@ var _this; var RIGHT_HAND = 1; var LEFT_HAND = 0; - var SPATIAL_CONTROLLERS_PER_PALM = 2; - var TIP_CONTROLLER_OFFSET = 1; var MIN_POINT_DISTANCE = 0.02; var MAX_POINT_DISTANCE = 0.5; var MAX_POINTS_PER_LINE = 40; @@ -58,7 +56,9 @@ this.getHandRotation = MyAvatar.getLeftPalmRotation; this.triggerAction = Controller.findAction("LEFT_HAND_CLICK"); } - Overlays.editOverlay(this.laserPointer, {visible: true}); + Overlays.editOverlay(this.laserPointer, { + visible: true + }); }, continueFarGrabbingNonColliding: function() { @@ -69,13 +69,14 @@ }; this.intersection = Entities.findRayIntersection(pickRay, true, this.whitelist); + if (this.intersection.intersects) { var distance = Vec3.distance(handPosition, this.intersection.intersection); if (distance < MAX_DISTANCE) { this.triggerValue = Controller.getActionValue(this.triggerAction); this.currentStrokeWidth = map(this.triggerValue, 0, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); var displayPoint = this.intersection.intersection; - displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.intersection.surfaceNormal, -.01)); + displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.intersection.surfaceNormal, -0.01)); Overlays.editOverlay(this.laserPointer, { position: displayPoint, size: { @@ -90,11 +91,22 @@ this.oldPosition = null; } } - } else { - this.releaseGrab(); + } else if(this.intersection.properties.type !== "Unknown") { + //If type is unknown, ignore + print("entity name " + this.intersection.properties.type); + this.stopPainting(); } }, + stopPainting: function() { + this.painting = false; + Overlays.editOverlay(this.laserPointer, { + visible: false + }); + this.oldPosition = null; + print("STOP PAINTING"); + }, + paint: function(position, normal) { if (this.painting === false) { if (this.oldPosition) { @@ -108,11 +120,12 @@ var localPoint = Vec3.subtract(position, this.strokeBasePosition); //Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on - localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, 0.001 + Math.random() * .001)); //rand avoid z fighting - + localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, 0.001 + Math.random() * 0.001)); //rand avoid z fighting + this.oldPosition = position; var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]); if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) { //need a minimum distance to avoid binormal NANs + return; } if (this.strokePoints.length > 0 && distance > MAX_POINT_DISTANCE) { @@ -140,7 +153,6 @@ this.painting = false; return; } - this.oldPosition = position; }, @@ -168,9 +180,9 @@ }, releaseGrab: function() { - this.painting = false; - Overlays.editOverlay(this.laserPointer, {visible: false}); - this.oldPosition = null; + print("RELEASE"); + this.stopPainting(); + }, changeColor: function() { @@ -184,7 +196,7 @@ var entities = Entities.findEntities(this.position, 5); entities.forEach(function(entity) { var name = Entities.getEntityProperties(entity, "name").name; - if(name === "paintStroke") { + if (name === "paintStroke") { Entities.deleteEntity(entity); } }); diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 32e049aef6..43a490a67e 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -11,8 +11,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ - - +// Script specific +/*global hexToRgb */ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteBoardEntityScript.js?v2"); @@ -38,6 +38,7 @@ var colors = [ ]; var whiteboard = Entities.addEntity({ type: "Box", + name: "whiteboard", position: center, rotation: rotation, script: scriptURL, @@ -61,7 +62,7 @@ var colorBoxes = []; var colorSquareDimensions = { x: (whiteboardDimensions.x / 2) / (colors.length - 1), - y: .1, + y: 0.1, z: 0.05 }; colorBoxPosition.y += whiteboardDimensions.y / 2 + colorSquareDimensions.y / 2 - 0.01; @@ -76,7 +77,6 @@ for (var i = 0; i < colors.length; i++) { color: colors[i], script: scriptURL, userData: JSON.stringify({ - colorPalette: true, whiteboard: whiteboard }) }); @@ -84,6 +84,21 @@ for (var i = 0; i < colors.length; i++) { colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); } +var blackBoxDimensions = {x: .2, y: .2, z: 0.05}; +colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x/2 - 0.01)); +colorBoxPosition.y += 0.3; +var blackBox = Entities.addEntity({ + type: 'Box', + position: colorBoxPosition, + dimensions: blackBoxDimensions, + rotation: rotation, + color: {red: 0, green: 0, blue: 0}, + script: scriptURL, + userData: JSON.stringify({ + whiteboard: whiteboard + }) +}) + var eraseBoxDimensions = { x: 0.5, @@ -124,6 +139,7 @@ print(JSON.stringify(Entities.getEntityProperties(eraseAllText))) function cleanup() { Entities.deleteEntity(whiteboard); Entities.deleteEntity(eraseAllText); + Entities.deleteEntity(blackBox); colorBoxes.forEach(function(colorBox) { Entities.deleteEntity(colorBox); }); From 1a9c1110c0fe30e61d4ecdaed2b7718710d6fc6d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:17:54 -0700 Subject: [PATCH 0099/1003] Fixed gap between strokes --- examples/painting/whiteboard/whiteboardEntityScript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 59977fe52a..08cca8a047 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -121,7 +121,6 @@ var localPoint = Vec3.subtract(position, this.strokeBasePosition); //Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, 0.001 + Math.random() * 0.001)); //rand avoid z fighting - this.oldPosition = position; var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]); if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) { //need a minimum distance to avoid binormal NANs @@ -153,6 +152,7 @@ this.painting = false; return; } + this.oldPosition = position; }, From d072a2efd37859cb8b8827f26f4f156596c8d56b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:26:38 -0700 Subject: [PATCH 0100/1003] laser pointer now showing up all the time --- examples/painting/whiteboard/whiteboardEntityScript.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 08cca8a047..122e1b2e38 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -69,6 +69,7 @@ }; this.intersection = Entities.findRayIntersection(pickRay, true, this.whitelist); + //Comment out above line and uncomment below line to see difference in performance between using a whitelist, and not using one if (this.intersection.intersects) { var distance = Vec3.distance(handPosition, this.intersection.intersection); @@ -76,7 +77,7 @@ this.triggerValue = Controller.getActionValue(this.triggerAction); this.currentStrokeWidth = map(this.triggerValue, 0, 1, MIN_STROKE_WIDTH, MAX_STROKE_WIDTH); var displayPoint = this.intersection.intersection; - displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.intersection.surfaceNormal, -0.01)); + displayPoint = Vec3.sum(displayPoint, Vec3.multiply(this.normal, 0.01)); Overlays.editOverlay(this.laserPointer, { position: displayPoint, size: { @@ -92,8 +93,7 @@ } } } else if(this.intersection.properties.type !== "Unknown") { - //If type is unknown, ignore - print("entity name " + this.intersection.properties.type); + //Sometimes ray will pick against an invisible object with type unkown... so if type is unknown, ignore this.stopPainting(); } }, @@ -104,7 +104,6 @@ visible: false }); this.oldPosition = null; - print("STOP PAINTING"); }, paint: function(position, normal) { @@ -180,7 +179,6 @@ }, releaseGrab: function() { - print("RELEASE"); this.stopPainting(); }, From 7fb6b7e27283d1a51e66936f92cc43a34525949b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:31:09 -0700 Subject: [PATCH 0101/1003] Reverted hand controller --- examples/controllers/handControllerGrab.js | 2 +- examples/painting/whiteboard/whiteboardEntityScript.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 0f59e80948..0b761c6559 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -67,7 +67,7 @@ var MSEC_PER_SEC = 1000.0; var startTime = Date.now(); var LIFETIME = 10; var ACTION_LIFETIME = 10; // seconds -var PICKS_PER_SECOND_PER_HAND = 60; +var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; // states for the state machine diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 122e1b2e38..0cfa12746a 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -70,6 +70,7 @@ this.intersection = Entities.findRayIntersection(pickRay, true, this.whitelist); //Comment out above line and uncomment below line to see difference in performance between using a whitelist, and not using one + // this.intersection = Entities.findRayIntersection(pickRay, true); if (this.intersection.intersects) { var distance = Vec3.distance(handPosition, this.intersection.intersection); From 880d34d483c323dcaca522bf5d0ec7640a9c6310 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Oct 2015 20:35:15 +0200 Subject: [PATCH 0102/1003] JSConsole improvements - ability to set Script Engine - clear function for clearing the output - only show evaluation output for executeCommand, print() and error output is still being printed --- interface/src/ui/JSConsole.cpp | 63 +++++++++++++++++++++++++--------- interface/src/ui/JSConsole.h | 6 +++- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 6e5b22399d..6a8b1116a7 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -35,7 +35,8 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : _ui(new Ui::Console), _currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND), _commandHistory(), - _scriptEngine(scriptEngine) { + _ownScriptEngine(scriptEngine == NULL), + _scriptEngine(NULL) { _ui->setupUi(this); _ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap); @@ -51,23 +52,40 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : connect(_ui->scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int, int)), this, SLOT(scrollToBottom())); connect(_ui->promptTextEdit, SIGNAL(textChanged()), this, SLOT(resizeTextInput())); - - if (_scriptEngine == NULL) { - _scriptEngine = qApp->loadScript(QString(), false); - } - - connect(_scriptEngine, SIGNAL(evaluationFinished(QScriptValue, bool)), - this, SLOT(handleEvalutationFinished(QScriptValue, bool))); - connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); - connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + setScriptEngine(scriptEngine); resizeTextInput(); } JSConsole::~JSConsole() { + disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + if (_ownScriptEngine) { + _scriptEngine->deleteLater(); + } delete _ui; } +void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { + if (_scriptEngine == scriptEngine && scriptEngine != NULL) { + return; + } + if (_scriptEngine != NULL) { + disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); + if (_ownScriptEngine) { + _scriptEngine->deleteLater(); + } + } + + // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine + _ownScriptEngine = scriptEngine == NULL; + _scriptEngine = _ownScriptEngine ? qApp->loadScript(QString(), false) : scriptEngine; + + connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); + connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); +} + void JSConsole::executeCommand(const QString& command) { _commandHistory.prepend(command); if (_commandHistory.length() > MAX_HISTORY_SIZE) { @@ -78,12 +96,10 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); - QMetaObject::invokeMethod(_scriptEngine, "evaluate", Q_ARG(const QString&, command)); + QScriptValue result; + QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, + Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command)); - resetCurrentCommandHistory(); -} - -void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) { _ui->promptTextEdit->setDisabled(false); // Make sure focus is still on this window - some commands are blocking and can take awhile to execute. @@ -91,10 +107,13 @@ void JSConsole::handleEvalutationFinished(QScriptValue result, bool isException) _ui->promptTextEdit->setFocus(); } - QString gutter = (isException || result.isError()) ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND; - QString resultColor = (isException || result.isError()) ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE; + bool error = (_scriptEngine->hasUncaughtException() || result.isError()); + QString gutter = error ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND; + QString resultColor = error ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE; QString resultStr = "" + result.toString().toHtmlEscaped() + ""; appendMessage(gutter, resultStr); + + resetCurrentCommandHistory(); } void JSConsole::handleError(const QString& message) { @@ -233,3 +252,13 @@ void JSConsole::appendMessage(const QString& gutter, const QString& message) { _ui->logArea->updateGeometry(); scrollToBottom(); } + +void JSConsole::clear() { + QLayoutItem* item; + while ((item = _ui->logArea->layout()->takeAt(0)) != NULL) { + delete item->widget(); + delete item; + } + _ui->logArea->updateGeometry(); + scrollToBottom(); +} diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index 98afdf7bf8..dd104723d0 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -31,6 +31,9 @@ public: JSConsole(QWidget* parent, ScriptEngine* scriptEngine = NULL); ~JSConsole(); + void setScriptEngine(ScriptEngine* scriptEngine = NULL); + void clear(); + public slots: void executeCommand(const QString& command); @@ -47,7 +50,6 @@ protected: protected slots: void scrollToBottom(); void resizeTextInput(); - void handleEvalutationFinished(QScriptValue result, bool isException); void handlePrint(const QString& message); void handleError(const QString& message); @@ -60,6 +62,8 @@ private: Ui::Console* _ui; int _currentCommandInHistory; QList _commandHistory; + // Keeps track if the script engine is created inside the JSConsole + bool _ownScriptEngine; QString _rootCommand; ScriptEngine* _scriptEngine; }; From 7a8db803eb2199bed4a30c97ec7e3df355d6bf0e Mon Sep 17 00:00:00 2001 From: James Pollack Date: Wed, 14 Oct 2015 19:35:37 +0100 Subject: [PATCH 0103/1003] move toolwindow tabs to the top --- interface/src/scripting/WebWindowClass.cpp | 2 +- interface/src/ui/ToolWindow.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index f9dc9e07ae..c78312e57a 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -52,7 +52,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid dockWidget->setWidget(_webView); - toolWindow->addDockWidget(Qt::RightDockWidgetArea, dockWidget); + toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal); _windowWidget = dockWidget; } else { diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 95dd522415..78131c9a95 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -118,7 +118,18 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) } void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation) { - QMainWindow::addDockWidget(area, dockWidget, orientation); + QList dockWidgets = findChildren(); + + setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North); + + QMainWindow::addDockWidget(area, dockWidget, orientation); + + // We want to force tabbing, so retabify all of our widgets. + QDockWidget* lastDockWidget = dockWidget; + foreach(QDockWidget* nextDockWidget, dockWidgets) { + tabifyDockWidget(lastDockWidget, nextDockWidget); + lastDockWidget = nextDockWidget; + } connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated); } From 96b6067d88be891ee5a35806447bb2730960612e Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Oct 2015 20:37:47 +0200 Subject: [PATCH 0104/1003] ScriptEditor Improvements: - Script restart works proper again - Fixed on and off icon switch - Replaced regular script output box with fully functional JSConsole! --- interface/src/ui/ScriptEditorWidget.cpp | 33 ++++++++-------------- interface/src/ui/ScriptEditorWidget.h | 4 +-- interface/src/ui/ScriptEditorWindow.cpp | 12 +++----- interface/ui/scriptEditorWidget.ui | 37 ------------------------- 4 files changed, 18 insertions(+), 68 deletions(-) diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index 76327804b6..0ae13f9c24 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -52,10 +52,16 @@ ScriptEditorWidget::ScriptEditorWidget() : // We create a new ScriptHighligting QObject and provide it with a parent so this is NOT a memory leak. new ScriptHighlighting(_scriptEditorWidgetUI->scriptEdit->document()); QTimer::singleShot(0, _scriptEditorWidgetUI->scriptEdit, SLOT(setFocus())); + + _console = new JSConsole(this); + _console->setFixedHeight(CONSOLE_HEIGHT); + _scriptEditorWidgetUI->verticalLayout->addWidget(_console); + connect(_scriptEditorWidgetUI->clearButton, &QPushButton::clicked, _console, &JSConsole::clear); } ScriptEditorWidget::~ScriptEditorWidget() { delete _scriptEditorWidgetUI; + delete _console; } void ScriptEditorWidget::onScriptModified() { @@ -68,6 +74,7 @@ void ScriptEditorWidget::onScriptModified() { void ScriptEditorWidget::onScriptFinished(const QString& scriptPath) { _scriptEngine = NULL; + _console->setScriptEngine(NULL); if (_isRestarting) { _isRestarting = false; setRunning(true); @@ -89,8 +96,6 @@ bool ScriptEditorWidget::setRunning(bool run) { if (_scriptEngine != NULL) { disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } @@ -100,15 +105,15 @@ bool ScriptEditorWidget::setRunning(bool run) { // Reload script so that an out of date copy is not retrieved from the cache _scriptEngine = qApp->loadScript(scriptURLString, true, true, false, true); connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } else { connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); - qApp->stopScript(_currentScript); + const QString& scriptURLString = QUrl(_currentScript).toString(); + qApp->stopScript(scriptURLString); _scriptEngine = NULL; } + _console->setScriptEngine(_scriptEngine); return true; } @@ -147,8 +152,6 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { if (_scriptEngine != NULL) { disconnect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); disconnect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } @@ -168,16 +171,14 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { static_cast(this->parent()->parent()->parent())->terminateCurrentTab(); } } - const QString& scriptURLString = QUrl(_currentScript).toString(); _scriptEngine = qApp->getScriptEngine(scriptURLString); if (_scriptEngine != NULL) { connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); - connect(_scriptEngine, &ScriptEngine::errorMessage, this, &ScriptEditorWidget::onScriptError); - connect(_scriptEngine, &ScriptEngine::printedMessage, this, &ScriptEditorWidget::onScriptPrint); connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } + _console->setScriptEngine(_scriptEngine); } bool ScriptEditorWidget::save() { @@ -210,19 +211,11 @@ bool ScriptEditorWidget::questionSave() { QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Interface"), tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); - return button == QMessageBox::Save ? save() : (button == QMessageBox::Cancel ? false : true); + return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard); } return true; } -void ScriptEditorWidget::onScriptError(const QString& message) { - _scriptEditorWidgetUI->debugText->appendPlainText("ERROR: " + message); -} - -void ScriptEditorWidget::onScriptPrint(const QString& message) { - _scriptEditorWidgetUI->debugText->appendPlainText("> " + message); -} - void ScriptEditorWidget::onWindowActivated() { if (!_isReloading) { _isReloading = true; @@ -241,10 +234,8 @@ void ScriptEditorWidget::onWindowActivated() { setRunning(false); // Script is restarted once current script instance finishes. } - } } - _isReloading = false; } } diff --git a/interface/src/ui/ScriptEditorWidget.h b/interface/src/ui/ScriptEditorWidget.h index 8dd847ee6d..f53fd7b718 100644 --- a/interface/src/ui/ScriptEditorWidget.h +++ b/interface/src/ui/ScriptEditorWidget.h @@ -14,6 +14,7 @@ #include +#include "JSConsole.h" #include "ScriptEngine.h" namespace Ui { @@ -47,12 +48,11 @@ public slots: void onWindowActivated(); private slots: - void onScriptError(const QString& message); - void onScriptPrint(const QString& message); void onScriptModified(); void onScriptFinished(const QString& scriptName); private: + JSConsole* _console; Ui::ScriptEditorWidget* _scriptEditorWidgetUI; ScriptEngine* _scriptEngine; QString _currentScript; diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index b5c8500083..3c6d0f73d5 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -28,7 +28,6 @@ #include #include "Application.h" -#include "JSConsole.h" #include "PathUtils.h" ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : @@ -59,10 +58,6 @@ ScriptEditorWindow::ScriptEditorWindow(QWidget* parent) : _ScriptEditorWindowUI->newButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/new-script.svg"))); _ScriptEditorWindowUI->saveButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/save-script.svg"))); _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + "icons/start-script.svg"))); - - QWidget* console = new JSConsole(this); - console->setFixedHeight(CONSOLE_HEIGHT); - this->layout()->addWidget(console); } ScriptEditorWindow::~ScriptEditorWindow() { @@ -77,10 +72,11 @@ void ScriptEditorWindow::setRunningState(bool run) { } void ScriptEditorWindow::updateButtons() { + bool isRunning = _ScriptEditorWindowUI->tabWidget->currentIndex() != -1 && + static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning(); _ScriptEditorWindowUI->toggleRunButton->setEnabled(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1); - _ScriptEditorWindowUI->toggleRunButton->setIcon(_ScriptEditorWindowUI->tabWidget->currentIndex() != -1 && - static_cast(_ScriptEditorWindowUI->tabWidget->currentWidget())->isRunning() ? - QIcon("../resources/icons/stop-script.svg") : QIcon("../resources/icons/start-script.svg")); + _ScriptEditorWindowUI->toggleRunButton->setIcon(QIcon(QPixmap(PathUtils::resourcesPath() + ((isRunning ? + "icons/stop-script.svg" : "icons/start-script.svg"))))); } void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { diff --git a/interface/ui/scriptEditorWidget.ui b/interface/ui/scriptEditorWidget.ui index 8aeeff363f..e2e538a595 100644 --- a/interface/ui/scriptEditorWidget.ui +++ b/interface/ui/scriptEditorWidget.ui @@ -128,25 +128,6 @@ - - - - - 0 - 0 - - - - font: 15px "Courier"; - - - true - - - - - - @@ -158,22 +139,4 @@ - - - clearButton - clicked() - debugText - clear() - - - 663 - 447 - - - 350 - 501 - - - - From 6cd9bf407f9fb7d59fec862553fefbc275bf414f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:40:06 -0700 Subject: [PATCH 0105/1003] Reverted handControllerGrab --- examples/controllers/handControllerGrab.js | 3 +-- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 0b761c6559..d8d7934191 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -177,7 +177,6 @@ function MyController(hand, triggerAction) { this.continueFarGrabbingNonColliding(); break; case STATE_RELEASE: - print("TRIGGER VALUE " + this.triggerValue); this.release(); break; } @@ -254,7 +253,6 @@ function MyController(hand, triggerAction) { return; } - // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); var pickRay = { @@ -570,6 +568,7 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 43a490a67e..3e89775ff0 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -146,4 +146,4 @@ function cleanup() { } -Script.scriptEnding.connect(cleanup); \ No newline at end of file +// Script.scriptEnding.connect(cleanup); \ No newline at end of file From fa2bf2b2d9f47335888dfcc1b4dda4384c9a0fb9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 11:43:03 -0700 Subject: [PATCH 0106/1003] tighten up locking in actions --- interface/src/avatar/AvatarActionHold.cpp | 179 ++++++++++-------- .../entities/src/EntityActionInterface.h | 20 +- libraries/entities/src/EntityItem.cpp | 5 +- libraries/physics/src/ObjectAction.cpp | 25 ++- 4 files changed, 135 insertions(+), 94 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 60f1b31625..7d38eb0f23 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -37,37 +37,41 @@ AvatarActionHold::~AvatarActionHold() { } void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - QSharedPointer avatarManager = DependencyManager::get(); - AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); - std::shared_ptr holdingAvatar = std::static_pointer_cast(holdingAvatarData); + bool gotLock = false; + glm::quat rotation; + glm::vec3 position; + std::shared_ptr holdingAvatar = nullptr; + + gotLock = withTryReadLock([&]{ + QSharedPointer avatarManager = DependencyManager::get(); + AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); + holdingAvatar = std::static_pointer_cast(holdingAvatarData); + + if (holdingAvatar) { + glm::vec3 offset; + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = holdingAvatar->getRightPalmPosition(); + palmRotation = holdingAvatar->getRightPalmRotation(); + } else { + palmPosition = holdingAvatar->getLeftPalmPosition(); + palmRotation = holdingAvatar->getLeftPalmRotation(); + } + + rotation = palmRotation * _relativeRotation; + offset = rotation * _relativePosition; + position = palmPosition + offset; + } + }); if (holdingAvatar) { - glm::quat rotation; - glm::vec3 position; - glm::vec3 offset; - bool gotLock = withTryReadLock([&]{ - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = holdingAvatar->getRightPalmPosition(); - palmRotation = holdingAvatar->getRightPalmRotation(); - } else { - palmPosition = holdingAvatar->getLeftPalmPosition(); - palmRotation = holdingAvatar->getLeftPalmRotation(); - } - - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; - }); - if (gotLock) { gotLock = withTryWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - }); + _positionalTarget = position; + _rotationalTarget = rotation; + }); } - if (gotLock) { ObjectActionSpring::updateActionWorker(deltaTimeStep); } @@ -76,46 +80,61 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { bool AvatarActionHold::updateArguments(QVariantMap arguments) { - if (!ObjectAction::updateArguments(arguments)) { - return false; - } - bool ok = true; - glm::vec3 relativePosition = - EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); - if (!ok) { - relativePosition = _relativePosition; - } + glm::vec3 relativePosition; + glm::quat relativeRotation; + float timeScale; + QString hand; + QUuid holderID; + bool needUpdate = false; + bool updateArgumentsSuceeded = true; - ok = true; - glm::quat relativeRotation = - EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); - if (!ok) { - relativeRotation = _relativeRotation; - } + withReadLock([&]{ + if (!ObjectAction::updateArguments(arguments)) { + updateArgumentsSuceeded = false; + return; + } + bool ok = true; + relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); + if (!ok) { + relativePosition = _relativePosition; + } - ok = true; - float timeScale = - EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); - if (!ok) { - timeScale = _linearTimeScale; - } + ok = true; + relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); + if (!ok) { + relativeRotation = _relativeRotation; + } - ok = true; - QString hand = - EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); - if (!ok || !(hand == "left" || hand == "right")) { - hand = _hand; - } + ok = true; + timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); + if (!ok) { + timeScale = _linearTimeScale; + } - ok = true; - auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto holderID = myAvatar->getSessionUUID(); + ok = true; + hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); + if (!ok || !(hand == "left" || hand == "right")) { + hand = _hand; + } - if (relativePosition != _relativePosition + ok = true; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + holderID = myAvatar->getSessionUUID(); + + if (relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale || hand != _hand || holderID != _holderID) { + needUpdate = true; + } + }); + + if (!updateArgumentsSuceeded) { + return false; + } + + if (needUpdate) { withWriteLock([&] { _relativePosition = relativePosition; _relativeRotation = relativeRotation; @@ -154,18 +173,20 @@ QByteArray AvatarActionHold::serialize() const { QByteArray serializedActionArguments; QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); - dataStream << ACTION_TYPE_HOLD; - dataStream << getID(); - dataStream << AvatarActionHold::holdVersion; + withReadLock([&]{ + dataStream << ACTION_TYPE_HOLD; + dataStream << getID(); + dataStream << AvatarActionHold::holdVersion; - dataStream << _holderID; - dataStream << _relativePosition; - dataStream << _relativeRotation; - dataStream << _linearTimeScale; - dataStream << _hand; + dataStream << _holderID; + dataStream << _relativePosition; + dataStream << _relativeRotation; + dataStream << _linearTimeScale; + dataStream << _hand; - dataStream << _expires; - dataStream << _tag; + dataStream << _expires; + dataStream << _tag; + }); return serializedActionArguments; } @@ -187,15 +208,21 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) { return; } - dataStream >> _holderID; - dataStream >> _relativePosition; - dataStream >> _relativeRotation; - dataStream >> _linearTimeScale; - _angularTimeScale = _linearTimeScale; - dataStream >> _hand; + withWriteLock([&]{ + dataStream >> _holderID; + dataStream >> _relativePosition; + dataStream >> _relativeRotation; + dataStream >> _linearTimeScale; + _angularTimeScale = _linearTimeScale; + dataStream >> _hand; - dataStream >> _expires; - dataStream >> _tag; + dataStream >> _expires; + dataStream >> _tag; - _active = true; + qDebug() << "deserialize hold: " << _holderID + << _relativePosition.x << _relativePosition.y << _relativePosition.z + << _hand; + + _active = true; + }); } diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 44c269f336..49739e7da9 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -53,16 +53,6 @@ public: virtual bool shouldSuppressLocationEdits() { return false; } -protected: - virtual glm::vec3 getPosition() = 0; - virtual void setPosition(glm::vec3 position) = 0; - virtual glm::quat getRotation() = 0; - virtual void setRotation(glm::quat rotation) = 0; - virtual glm::vec3 getLinearVelocity() = 0; - virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0; - virtual glm::vec3 getAngularVelocity() = 0; - virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0; - // these look in the arguments map for a named argument. if it's not found or isn't well formed, // ok will be set to false (note that it's never set to true -- set it to true before calling these). // if required is true, failure to extract an argument will cause a warning to be printed. @@ -77,6 +67,16 @@ protected: static QString extractStringArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); +protected: + virtual glm::vec3 getPosition() = 0; + virtual void setPosition(glm::vec3 position) = 0; + virtual glm::quat getRotation() = 0; + virtual void setRotation(glm::quat rotation) = 0; + virtual glm::vec3 getLinearVelocity() = 0; + virtual void setLinearVelocity(glm::vec3 linearVelocity) = 0; + virtual glm::vec3 getAngularVelocity() = 0; + virtual void setAngularVelocity(glm::vec3 angularVelocity) = 0; + QUuid _id; EntityActionType _type; }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5b72ac62c1..56d68e910e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1630,8 +1630,6 @@ void EntityItem::deserializeActionsInternal() { return; } - // Keep track of which actions got added or updated by the new actionData - EntityTreePointer entityTree = _element ? _element->getTree() : nullptr; assert(entityTree); EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr; @@ -1643,6 +1641,7 @@ void EntityItem::deserializeActionsInternal() { serializedActionsStream >> serializedActions; } + // Keep track of which actions got added or updated by the new actionData QSet updated; foreach(QByteArray serializedAction, serializedActions) { @@ -1658,12 +1657,14 @@ void EntityItem::deserializeActionsInternal() { updated << actionID; if (_objectActions.contains(actionID)) { + qDebug() << "EntityItem::deserializeActionsInternal is UPDATING" << actionID; EntityActionPointer action = _objectActions[actionID]; // TODO: make sure types match? there isn't currently a way to // change the type of an existing action. action->deserialize(serializedAction); action->locallyAddedButNotYetReceived = false; } else { + qDebug() << "EntityItem::deserializeActionsInternal is ADDING" << actionID; auto actionFactory = DependencyManager::get(); EntityItemPointer entity = shared_from_this(); EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction); diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 82395c21cf..2923bc20a9 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -24,20 +24,33 @@ ObjectAction::~ObjectAction() { } void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep) { - if (_ownerEntity.expired()) { + bool ownerEntityExpired = false; + quint64 expiresWhen = 0; + + withReadLock([&]{ + ownerEntityExpired = _ownerEntity.expired(); + expiresWhen = _expires; + }); + + if (ownerEntityExpired) { qDebug() << "warning -- action with no entity removing self from btCollisionWorld."; btDynamicsWorld* dynamicsWorld = static_cast(collisionWorld); dynamicsWorld->removeAction(this); return; } - if (_expires > 0) { + if (expiresWhen > 0) { quint64 now = usecTimestampNow(); - if (now > _expires) { - EntityItemPointer ownerEntity = _ownerEntity.lock(); - _active = false; + if (now > expiresWhen) { + EntityItemPointer ownerEntity = nullptr; + QUuid myID; + withWriteLock([&]{ + ownerEntity = _ownerEntity.lock(); + _active = false; + myID = getID(); + }); if (ownerEntity) { - ownerEntity->removeAction(nullptr, getID()); + ownerEntity->removeAction(nullptr, myID); } } } From 22615dae0753490fe1f135f335cb1982387acba3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:43:05 -0700 Subject: [PATCH 0107/1003] Don't delete board on script close --- examples/painting/whiteboard/whiteboardSpawner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 3e89775ff0..f4ce57ca50 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -146,4 +146,5 @@ function cleanup() { } +// Uncomment this line to delete whiteboard and all associated entity on script close // Script.scriptEnding.connect(cleanup); \ No newline at end of file From 0991be32557f4cd4596b00d6f8ad33bc16fbbf55 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:44:47 -0700 Subject: [PATCH 0108/1003] got rid of printing statements --- examples/painting/whiteboard/whiteboardSpawner.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index f4ce57ca50..f6f32eaa4d 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -132,7 +132,6 @@ var eraseAllText = Entities.addEntity({ whiteboard: whiteboard }) }); -print(JSON.stringify(Entities.getEntityProperties(eraseAllText))) From c63ef10c67e11af0816d3a3e20b71bb9b06e9a0f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 11:50:35 -0700 Subject: [PATCH 0109/1003] got rid of query string --- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index f6f32eaa4d..02c8399ce7 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -15,7 +15,7 @@ /*global hexToRgb */ Script.include("../../libraries/utils.js"); -var scriptURL = Script.resolvePath("whiteBoardEntityScript.js?v2"); +var scriptURL = Script.resolvePath("whiteBoardEntityScript.js"); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); From c585647f10dfd7eb00973456120336053c583204 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Oct 2015 21:00:50 +0200 Subject: [PATCH 0110/1003] ScriptEditor Improvements: - fixed run icon --- interface/resources/icons/start-script.svg | 32 +++++++++++++++++----- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/interface/resources/icons/start-script.svg b/interface/resources/icons/start-script.svg index 1401853fe4..dc37144f7a 100644 --- a/interface/resources/icons/start-script.svg +++ b/interface/resources/icons/start-script.svg @@ -14,7 +14,7 @@ height="45" id="svg3827" version="1.1" - inkscape:version="0.48.1 r9760" + inkscape:version="0.48.3.1 r9886" sodipodi:docname="start-script.svg"> @@ -439,16 +439,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="5.6" - inkscape:cx="76.804753" - inkscape:cy="13.198134" + inkscape:zoom="22.4" + inkscape:cx="44.04179" + inkscape:cy="22.346221" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" - inkscape:window-width="1920" - inkscape:window-height="1028" + inkscape:window-width="1680" + inkscape:window-height="997" inkscape:window-x="-8" - inkscape:window-y="-8" + inkscape:window-y="21" inkscape:window-maximized="1" /> @@ -536,5 +536,23 @@ height="44.57473" x="84.498352" y="1050.0748" /> + + + + From db5f7cac8dc5e7f6de489c9fdcc63a2e764c48a9 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 12:03:10 -0700 Subject: [PATCH 0111/1003] Fixed name of whiteboard entity script URL --- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 02c8399ce7..8dc8be1cda 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -15,7 +15,7 @@ /*global hexToRgb */ Script.include("../../libraries/utils.js"); -var scriptURL = Script.resolvePath("whiteBoardEntityScript.js"); +var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); From 9d2ae661e16ea7797be7911f32d45015be8a8af9 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Oct 2015 21:10:52 +0200 Subject: [PATCH 0112/1003] removed some XML from start-script button which possible caused an error before --- interface/resources/icons/start-script.svg | 8 -------- 1 file changed, 8 deletions(-) diff --git a/interface/resources/icons/start-script.svg b/interface/resources/icons/start-script.svg index dc37144f7a..994eb61efe 100644 --- a/interface/resources/icons/start-script.svg +++ b/interface/resources/icons/start-script.svg @@ -540,14 +540,6 @@ id="run-arrow" transform="translate(-46.607143,-3.5714285)" inkscape:label="#run-arrow"> - Date: Wed, 14 Oct 2015 12:49:06 -0700 Subject: [PATCH 0113/1003] more lock fixing --- interface/src/avatar/AvatarActionHold.cpp | 15 +--- libraries/entities/src/EntityItem.cpp | 9 ++ libraries/physics/src/ObjectAction.cpp | 89 ++++++++++++-------- libraries/physics/src/ObjectActionOffset.cpp | 62 ++++++++------ libraries/physics/src/ObjectActionSpring.cpp | 82 ++++++++++-------- 5 files changed, 155 insertions(+), 102 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 7d38eb0f23..ce34306df4 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -86,13 +86,9 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { QString hand; QUuid holderID; bool needUpdate = false; - bool updateArgumentsSuceeded = true; + bool somethingChanged = ObjectAction::updateArguments(arguments); withReadLock([&]{ - if (!ObjectAction::updateArguments(arguments)) { - updateArgumentsSuceeded = false; - return; - } bool ok = true; relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); if (!ok) { @@ -121,7 +117,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { auto myAvatar = DependencyManager::get()->getMyAvatar(); holderID = myAvatar->getSessionUUID(); - if (relativePosition != _relativePosition + if (somethingChanged || + relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale || hand != _hand @@ -130,10 +127,6 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { } }); - if (!updateArgumentsSuceeded) { - return false; - } - if (needUpdate) { withWriteLock([&] { _relativePosition = relativePosition; @@ -221,7 +214,7 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) { qDebug() << "deserialize hold: " << _holderID << _relativePosition.x << _relativePosition.y << _relativePosition.z - << _hand; + << _hand << _expires; _active = true; }); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 56d68e910e..8fe5bea125 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1579,6 +1579,9 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s } EntityActionPointer action = _objectActions[actionID]; + + qDebug() << "EntityItem::removeActionInternal clearing _ownerEntity"; + action->setOwnerEntity(nullptr); _objectActions.remove(actionID); @@ -1601,6 +1604,7 @@ bool EntityItem::clearActions(EntitySimulation* simulation) { const QUuid id = i.key(); EntityActionPointer action = _objectActions[id]; i = _objectActions.erase(i); + qDebug() << "EntityItem::clearActions clearing _ownerEntity"; action->setOwnerEntity(nullptr); action->removeFromSimulation(simulation); } @@ -1644,6 +1648,8 @@ void EntityItem::deserializeActionsInternal() { // Keep track of which actions got added or updated by the new actionData QSet updated; + qDebug() << "EntityItem::deserializeActionsInternal" << serializedActions.size(); + foreach(QByteArray serializedAction, serializedActions) { QDataStream serializedActionStream(serializedAction); EntityActionType actionType; @@ -1722,8 +1728,11 @@ void EntityItem::setActionData(QByteArray actionData) { void EntityItem::setActionDataInternal(QByteArray actionData) { assertWriteLocked(); if (_allActionsDataCache != actionData) { + qDebug() << "EntityItem::setActionDataInternal yes"; _allActionsDataCache = actionData; deserializeActionsInternal(); + } else { + qDebug() << "EntityItem::setActionDataInternal no"; } checkWaitingToRemove(); } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 2923bc20a9..2f0de6d0ab 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -63,35 +63,48 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta } bool ObjectAction::updateArguments(QVariantMap arguments) { - bool lifetimeSet = true; - float lifetime = EntityActionInterface::extractFloatArgument("action", arguments, "lifetime", lifetimeSet, false); - if (lifetimeSet) { - quint64 now = usecTimestampNow(); - _expires = now + (quint64)(lifetime * USECS_PER_SECOND); - } else { - _expires = 0; - } + bool somethingChanged = false; - bool tagSet = true; - QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false); - if (tagSet) { - _tag = tag; - } else { - tag = ""; - } + withWriteLock([&]{ + quint64 previousExpires = _expires; + QString previousTag = _tag; - return true; + bool lifetimeSet = true; + float lifetime = EntityActionInterface::extractFloatArgument("action", arguments, "lifetime", lifetimeSet, false); + if (lifetimeSet) { + quint64 now = usecTimestampNow(); + _expires = now + (quint64)(lifetime * USECS_PER_SECOND); + } else { + _expires = 0; + } + + bool tagSet = true; + QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false); + if (tagSet) { + _tag = tag; + } else { + tag = ""; + } + + if (previousExpires != _expires || previousTag != _tag) { + somethingChanged = true; + } + }); + + return somethingChanged; } QVariantMap ObjectAction::getArguments() { QVariantMap arguments; - if (_expires == 0) { - arguments["lifetime"] = 0.0f; - } else { - quint64 now = usecTimestampNow(); - arguments["lifetime"] = (float)(_expires - now) / (float)USECS_PER_SECOND; - } - arguments["tag"] = _tag; + withReadLock([&]{ + if (_expires == 0) { + arguments["lifetime"] = 0.0f; + } else { + quint64 now = usecTimestampNow(); + arguments["lifetime"] = (float)(_expires - now) / (float)USECS_PER_SECOND; + } + arguments["tag"] = _tag; + }); return arguments; } @@ -100,20 +113,30 @@ void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { } void ObjectAction::removeFromSimulation(EntitySimulation* simulation) const { - simulation->removeAction(_id); + QUuid myID; + withReadLock([&]{ + myID = _id; + }); + simulation->removeAction(myID); } btRigidBody* ObjectAction::getRigidBody() { - auto ownerEntity = _ownerEntity.lock(); - if (!ownerEntity) { - return nullptr; + ObjectMotionState* motionState = nullptr; + withReadLock([&]{ + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return; + } + void* physicsInfo = ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + return; + } + motionState = static_cast(physicsInfo); + }); + if (motionState) { + return motionState->getRigidBody(); } - void* physicsInfo = ownerEntity->getPhysicsInfo(); - if (!physicsInfo) { - return nullptr; - } - ObjectMotionState* motionState = static_cast(physicsInfo); - return motionState->getRigidBody(); + return nullptr; } glm::vec3 ObjectAction::getPosition() { diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index 448ec34689..ad5f49b3fb 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -80,35 +80,46 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) { bool ObjectActionOffset::updateArguments(QVariantMap arguments) { - if (!ObjectAction::updateArguments(arguments)) { - return false; - } - bool ok = true; - glm::vec3 pointToOffsetFrom = - EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); - if (!ok) { - pointToOffsetFrom = _pointToOffsetFrom; - } + glm::vec3 pointToOffsetFrom; + float linearTimeScale; + float linearDistance; - ok = true; - float linearTimeScale = - EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false); - if (!ok) { - linearTimeScale = _linearTimeScale; - } + bool needUpdate = false; + bool somethingChanged = ObjectAction::updateArguments(arguments); - ok = true; - float linearDistance = - EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false); - if (!ok) { - linearDistance = _linearDistance; - } + withReadLock([&]{ + bool ok = true; + pointToOffsetFrom = + EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); + if (!ok) { + pointToOffsetFrom = _pointToOffsetFrom; + } - // only change stuff if something actually changed - if (_pointToOffsetFrom != pointToOffsetFrom - || _linearTimeScale != linearTimeScale - || _linearDistance != linearDistance) { + ok = true; + linearTimeScale = + EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false); + if (!ok) { + linearTimeScale = _linearTimeScale; + } + ok = true; + linearDistance = + EntityActionInterface::extractFloatArgument("offset action", arguments, "linearDistance", ok, false); + if (!ok) { + linearDistance = _linearDistance; + } + + // only change stuff if something actually changed + if (somethingChanged || + _pointToOffsetFrom != pointToOffsetFrom || + _linearTimeScale != linearTimeScale || + _linearDistance != linearDistance) { + needUpdate = true; + } + }); + + + if (needUpdate) { withWriteLock([&] { _pointToOffsetFrom = pointToOffsetFrom; _linearTimeScale = linearTimeScale; @@ -118,6 +129,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { activateBody(); }); } + return true; } diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 8b936a96e9..6c7507a9db 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -107,43 +107,52 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { const float MIN_TIMESCALE = 0.1f; + bool ObjectActionSpring::updateArguments(QVariantMap arguments) { - if (!ObjectAction::updateArguments(arguments)) { - return false; - } - // targets are required, spring-constants are optional - bool ok = true; - glm::vec3 positionalTarget = - EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false); - if (!ok) { - positionalTarget = _positionalTarget; - } - ok = true; - float linearTimeScale = - EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false); - if (!ok || linearTimeScale <= 0.0f) { - linearTimeScale = _linearTimeScale; - } + glm::vec3 positionalTarget; + float linearTimeScale; + glm::quat rotationalTarget; + float angularTimeScale; - ok = true; - glm::quat rotationalTarget = - EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false); - if (!ok) { - rotationalTarget = _rotationalTarget; - } + bool needUpdate = false; + bool somethingChanged = ObjectAction::updateArguments(arguments); + withReadLock([&]{ + // targets are required, spring-constants are optional + bool ok = true; + positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false); + if (!ok) { + positionalTarget = _positionalTarget; + } + ok = true; + linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false); + if (!ok || linearTimeScale <= 0.0f) { + linearTimeScale = _linearTimeScale; + } - ok = true; - float angularTimeScale = - EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false); - if (!ok) { - angularTimeScale = _angularTimeScale; - } + ok = true; + rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false); + if (!ok) { + rotationalTarget = _rotationalTarget; + } - if (positionalTarget != _positionalTarget - || linearTimeScale != _linearTimeScale - || rotationalTarget != _rotationalTarget - || angularTimeScale != _angularTimeScale) { - // something changed + ok = true; + angularTimeScale = + EntityActionInterface::extractFloatArgument("spring action", arguments, "angularTimeScale", ok, false); + if (!ok) { + angularTimeScale = _angularTimeScale; + } + + if (somethingChanged || + positionalTarget != _positionalTarget || + linearTimeScale != _linearTimeScale || + rotationalTarget != _rotationalTarget || + angularTimeScale != _angularTimeScale) { + // something changed + needUpdate = true; + } + }); + + if (needUpdate) { withWriteLock([&] { _positionalTarget = positionalTarget; _linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale)); @@ -151,8 +160,15 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale)); _active = true; activateBody(); + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } + }); } + return true; } From d72152da877283c16765e17d78ae0924d16161a1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 13:01:56 -0700 Subject: [PATCH 0114/1003] more locking --- interface/src/avatar/AvatarActionHold.cpp | 3 +-- libraries/physics/src/ObjectActionOffset.cpp | 7 ++++++- libraries/physics/src/ObjectActionSpring.cpp | 3 +-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index ce34306df4..59aebc23d8 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -136,15 +136,14 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { _angularTimeScale = _linearTimeScale; _hand = hand; _holderID = holderID; - _active = true; - activateBody(); auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { ownerEntity->setActionDataDirty(true); } }); + activateBody(); } return true; diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index ad5f49b3fb..4930d44f3c 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -126,8 +126,13 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { _linearDistance = linearDistance; _positionalTargetSet = true; _active = true; - activateBody(); + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } }); + activateBody(); } return true; diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 6c7507a9db..242849db6f 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -159,14 +159,13 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { _rotationalTarget = rotationalTarget; _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale)); _active = true; - activateBody(); auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { ownerEntity->setActionDataDirty(true); } - }); + activateBody(); } return true; From 9937b6a3ef93b8d779ce0e66ff2fe75e2d30bb2d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 13:12:11 -0700 Subject: [PATCH 0115/1003] quiet some debugging --- interface/src/avatar/AvatarActionHold.cpp | 4 +++- libraries/entities/src/EntityItem.cpp | 10 ---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 59aebc23d8..f12909e809 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -211,9 +211,11 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) { dataStream >> _expires; dataStream >> _tag; - qDebug() << "deserialize hold: " << _holderID + #if WANT_DEBUG + qDebug() << "deserialize AvatarActionHold: " << _holderID << _relativePosition.x << _relativePosition.y << _relativePosition.z << _hand << _expires; + #endif _active = true; }); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8fe5bea125..1ae0c481ac 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1580,8 +1580,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s EntityActionPointer action = _objectActions[actionID]; - qDebug() << "EntityItem::removeActionInternal clearing _ownerEntity"; - action->setOwnerEntity(nullptr); _objectActions.remove(actionID); @@ -1604,7 +1602,6 @@ bool EntityItem::clearActions(EntitySimulation* simulation) { const QUuid id = i.key(); EntityActionPointer action = _objectActions[id]; i = _objectActions.erase(i); - qDebug() << "EntityItem::clearActions clearing _ownerEntity"; action->setOwnerEntity(nullptr); action->removeFromSimulation(simulation); } @@ -1648,8 +1645,6 @@ void EntityItem::deserializeActionsInternal() { // Keep track of which actions got added or updated by the new actionData QSet updated; - qDebug() << "EntityItem::deserializeActionsInternal" << serializedActions.size(); - foreach(QByteArray serializedAction, serializedActions) { QDataStream serializedActionStream(serializedAction); EntityActionType actionType; @@ -1663,14 +1658,12 @@ void EntityItem::deserializeActionsInternal() { updated << actionID; if (_objectActions.contains(actionID)) { - qDebug() << "EntityItem::deserializeActionsInternal is UPDATING" << actionID; EntityActionPointer action = _objectActions[actionID]; // TODO: make sure types match? there isn't currently a way to // change the type of an existing action. action->deserialize(serializedAction); action->locallyAddedButNotYetReceived = false; } else { - qDebug() << "EntityItem::deserializeActionsInternal is ADDING" << actionID; auto actionFactory = DependencyManager::get(); EntityItemPointer entity = shared_from_this(); EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction); @@ -1728,11 +1721,8 @@ void EntityItem::setActionData(QByteArray actionData) { void EntityItem::setActionDataInternal(QByteArray actionData) { assertWriteLocked(); if (_allActionsDataCache != actionData) { - qDebug() << "EntityItem::setActionDataInternal yes"; _allActionsDataCache = actionData; deserializeActionsInternal(); - } else { - qDebug() << "EntityItem::setActionDataInternal no"; } checkWaitingToRemove(); } From 7df5ba01e6b4548f7ad146a83e8d9b262eeac4fe Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 14 Oct 2015 22:21:51 +0200 Subject: [PATCH 0116/1003] JSConsole: executeCommand works async now --- interface/src/ui/JSConsole.cpp | 18 ++++++++++++++++-- interface/src/ui/JSConsole.h | 8 ++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 6a8b1116a7..b50f84d538 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -9,9 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include #include +#include #include @@ -55,6 +57,8 @@ JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) : setScriptEngine(scriptEngine); resizeTextInput(); + + connect(&_executeWatcher, SIGNAL(finished()), this, SLOT(commandFinished())); } JSConsole::~JSConsole() { @@ -96,9 +100,19 @@ void JSConsole::executeCommand(const QString& command) { appendMessage(">", "" + command.toHtmlEscaped() + ""); + QFuture future = QtConcurrent::run(this, &JSConsole::executeCommandInWatcher, command); + _executeWatcher.setFuture(future); +} + +QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue result; QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command)); + return result; +} + +void JSConsole::commandFinished() { + QScriptValue result = _executeWatcher.result(); _ui->promptTextEdit->setDisabled(false); @@ -106,11 +120,11 @@ void JSConsole::executeCommand(const QString& command) { if (window()->isActiveWindow()) { _ui->promptTextEdit->setFocus(); } - + bool error = (_scriptEngine->hasUncaughtException() || result.isError()); QString gutter = error ? GUTTER_ERROR : GUTTER_PREVIOUS_COMMAND; QString resultColor = error ? RESULT_ERROR_STYLE : RESULT_SUCCESS_STYLE; - QString resultStr = "" + result.toString().toHtmlEscaped() + ""; + QString resultStr = "" + result.toString().toHtmlEscaped() + ""; appendMessage(gutter, resultStr); resetCurrentCommandHistory(); diff --git a/interface/src/ui/JSConsole.h b/interface/src/ui/JSConsole.h index dd104723d0..cb58beab35 100644 --- a/interface/src/ui/JSConsole.h +++ b/interface/src/ui/JSConsole.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -37,10 +38,6 @@ public: public slots: void executeCommand(const QString& command); -signals: - void commandExecuting(const QString& command); - void commandFinished(const QString& result); - protected: void setAndSelectCommand(const QString& command); virtual bool eventFilter(QObject* sender, QEvent* event); @@ -52,13 +49,16 @@ protected slots: void resizeTextInput(); void handlePrint(const QString& message); void handleError(const QString& message); + void commandFinished(); private: void appendMessage(const QString& gutter, const QString& message); void setToNextCommandInHistory(); void setToPreviousCommandInHistory(); void resetCurrentCommandHistory(); + QScriptValue executeCommandInWatcher(const QString& command); + QFutureWatcher _executeWatcher; Ui::Console* _ui; int _currentCommandInHistory; QList _commandHistory; From eb7b720945f385cdc318b9b67aed8dbdc5546877 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 13:24:22 -0700 Subject: [PATCH 0117/1003] got rid of whitespace --- 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 d8d7934191..af23c9278f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -568,7 +568,7 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } - + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; From 0b92bc11f6a47068cbc8814cd651138f824b473e Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Wed, 14 Oct 2015 13:26:10 -0700 Subject: [PATCH 0118/1003] fixed spacing --- 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 af23c9278f..d8d7934191 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -568,7 +568,7 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } - + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; From 815b263954da579feb22f49fd53ff2767cdc6a9c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 13:26:19 -0700 Subject: [PATCH 0119/1003] convert tabs to spaces --- interface/src/scripting/WebWindowClass.cpp | 2 +- interface/src/ui/ToolWindow.cpp | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index c78312e57a..7e998ed8ff 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -52,7 +52,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid dockWidget->setWidget(_webView); - toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal); + toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal); _windowWidget = dockWidget; } else { diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 78131c9a95..a1a108ab17 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -109,6 +109,7 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) // We want to force tabbing, so retabify all of our widgets. QDockWidget* lastDockWidget = dockWidget; + foreach (QDockWidget* nextDockWidget, dockWidgets) { tabifyDockWidget(lastDockWidget, nextDockWidget); lastDockWidget = nextDockWidget; @@ -118,18 +119,19 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) } void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation) { - QList dockWidgets = findChildren(); - - setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North); + QList dockWidgets = findChildren(); + + setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North); - QMainWindow::addDockWidget(area, dockWidget, orientation); + addDockWidget(area, dockWidget, orientation); - // We want to force tabbing, so retabify all of our widgets. - QDockWidget* lastDockWidget = dockWidget; - foreach(QDockWidget* nextDockWidget, dockWidgets) { - tabifyDockWidget(lastDockWidget, nextDockWidget); - lastDockWidget = nextDockWidget; - } + // We want to force tabbing, so retabify all of our widgets. + QDockWidget* lastDockWidget = dockWidget; + + foreach(QDockWidget* nextDockWidget, dockWidgets) { + tabifyDockWidget(lastDockWidget, nextDockWidget); + lastDockWidget = nextDockWidget; + } connect(dockWidget, &QDockWidget::visibilityChanged, this, &ToolWindow::onChildVisibilityUpdated); } From 9a2aa9a337761d2afd602db460db2f012e9233a7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 13:40:20 -0700 Subject: [PATCH 0120/1003] locking --- assignment-client/src/AssignmentAction.cpp | 16 ++++++++++++---- assignment-client/src/AssignmentAction.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/AssignmentAction.cpp b/assignment-client/src/AssignmentAction.cpp index 107a80cf14..388f4123f6 100644 --- a/assignment-client/src/AssignmentAction.cpp +++ b/assignment-client/src/AssignmentAction.cpp @@ -24,16 +24,24 @@ AssignmentAction::~AssignmentAction() { } void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const { - simulation->removeAction(_id); - simulation->applyActionChanges(); + withReadLock([&]{ + simulation->removeAction(_id); + simulation->applyActionChanges(); + }); } QByteArray AssignmentAction::serialize() const { - return _data; + QByteArray result; + withReadLock([&]{ + result = _data; + }); + return result; } void AssignmentAction::deserialize(QByteArray serializedArguments) { - _data = serializedArguments; + withWriteLock([&]{ + _data = serializedArguments; + }); } bool AssignmentAction::updateArguments(QVariantMap arguments) { diff --git a/assignment-client/src/AssignmentAction.h b/assignment-client/src/AssignmentAction.h index 23720bd465..6b901c9766 100644 --- a/assignment-client/src/AssignmentAction.h +++ b/assignment-client/src/AssignmentAction.h @@ -19,7 +19,7 @@ #include "EntityActionInterface.h" -class AssignmentAction : public EntityActionInterface { +class AssignmentAction : public EntityActionInterface, public ReadWriteLockable { public: AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity); virtual ~AssignmentAction(); From 619fce0d7fa5720b863c62c86e0878d93d11664a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 13:40:22 -0700 Subject: [PATCH 0121/1003] 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 d45f48c8cd1663b9633278fb33c89e1af5523126 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 13:55:04 -0700 Subject: [PATCH 0122/1003] fixbug --- interface/src/ui/ToolWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index a1a108ab17..bb62929846 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -123,7 +123,7 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North); - addDockWidget(area, dockWidget, orientation); + QMainWindow::addDockWidget(area, dockWidget, orientation); // We want to force tabbing, so retabify all of our widgets. QDockWidget* lastDockWidget = dockWidget; From 5ce39b74fdcf338ac56e9a0ef78fd4d8abe0694b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 13:58:19 -0700 Subject: [PATCH 0123/1003] debugging --- interface/src/Application.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0e5003830..7415e7d297 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2054,11 +2054,40 @@ void Application::checkFPS() { _timerStart.start(); } +uint64_t lastReport = usecTimestampNow(); // XXX + + void Application::idle() { if (_aboutToQuit) { return; // bail early, nothing to do here. } + + // XXX + { // XXX + uint64_t now = usecTimestampNow(); + if (now - lastReport > 4 * USECS_PER_SECOND) { + QSharedPointer avatarManager = DependencyManager::get(); + const AvatarHash& avatarHash = avatarManager->getAvatarHash(); + + glm::vec3 myLeftPalm = getMyAvatar()->getLeftPalmPosition(); + qDebug() << "my left palm:" << myLeftPalm; + + QHash::const_iterator i; + for (i = avatarHash.begin(); i != avatarHash.end(); ++i) { + std::shared_ptr otherAvatar = std::static_pointer_cast(i.value()); + if (otherAvatar->getSessionUUID() == getMyAvatar()->getSessionUUID()) { + continue; + } + glm::vec3 theirLeftPalm = otherAvatar->getLeftPalmPosition(); + qDebug() << "their left palm:" << theirLeftPalm; + }; + + lastReport = now; + } + } // XXX + // XXX + // depending on whether we're throttling or not. // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in // perpetuity and not expect events to get backed up. From 21519c6418d3566072b5784df5a7eb9e0fe5d833 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 14 Oct 2015 14:13:08 -0700 Subject: [PATCH 0124/1003] debugging --- interface/src/Application.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7415e7d297..733858dd7e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2066,12 +2066,14 @@ void Application::idle() { // XXX { // XXX uint64_t now = usecTimestampNow(); - if (now - lastReport > 4 * USECS_PER_SECOND) { + if (now - lastReport > 6 * USECS_PER_SECOND) { QSharedPointer avatarManager = DependencyManager::get(); const AvatarHash& avatarHash = avatarManager->getAvatarHash(); - glm::vec3 myLeftPalm = getMyAvatar()->getLeftPalmPosition(); - qDebug() << "my left palm:" << myLeftPalm; + qDebug() << "-----------------------"; + + qDebug() << "my left palm:" << getMyAvatar()->getLeftPalmPosition() + << " my chest:" << getMyAvatar()->getChestPosition(); QHash::const_iterator i; for (i = avatarHash.begin(); i != avatarHash.end(); ++i) { @@ -2079,8 +2081,8 @@ void Application::idle() { if (otherAvatar->getSessionUUID() == getMyAvatar()->getSessionUUID()) { continue; } - glm::vec3 theirLeftPalm = otherAvatar->getLeftPalmPosition(); - qDebug() << "their left palm:" << theirLeftPalm; + qDebug() << "their left palm:" << otherAvatar->getLeftPalmPosition() + << " their chest:" << otherAvatar->getChestPosition(); }; lastReport = now; From 3e7a6fd490cfbd1bf327d71d4d6a6b6d30389963 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 14 Oct 2015 14:43:59 -0700 Subject: [PATCH 0125/1003] Change to boom offset for 3rd person HMD camera. Previously the boom offset was computed in HMD sensor space instead of world space, so it did not play well with the vive, or the oculus if you weren't facing the camera directly. Now it is computed in world space, and is always behind the character's position/orientation. This can cause the boom to swing if the character rotation changes but now the avatar rotation in HMD uses comfort mode. It's not that disorienting. --- interface/src/Application.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e0e5003830..c9a07c1d13 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1109,13 +1109,10 @@ void Application::paintGL() { } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { - glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); - _myCamera.setRotation(myAvatar->getWorldAlignedOrientation() * hmdRotation); - // Ignore MenuOption::CenterPlayerInView in HMD view - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(myAvatar->getDefaultEyePosition() - + myAvatar->getOrientation() - * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f) + hmdOffset)); + auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + _myCamera.setRotation(glm::normalize(glm::quat_cast(hmdWorldMat))); + auto worldBoomOffset = myAvatar->getOrientation() * (myAvatar->getScale() * myAvatar->getBoomLength() * glm::vec3(0.0f, 0.0f, 1.0f)); + _myCamera.setPosition(extractTranslation(hmdWorldMat) + worldBoomOffset); } else { _myCamera.setRotation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { From 536351e9d5053d731bab08b3a910fb8d071379c6 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 15:10:04 -0700 Subject: [PATCH 0126/1003] move set tab position to constructor --- interface/src/ui/ToolWindow.cpp | 5 ++--- interface/src/ui/ToolWindow.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index bb62929846..de4b3887db 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -22,6 +22,8 @@ ToolWindow::ToolWindow(QWidget* parent) : _hasShown(false), _lastGeometry() { + setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North); + # ifndef Q_OS_LINUX setDockOptions(QMainWindow::ForceTabbedDocks); # endif @@ -120,12 +122,9 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget) void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation) { QList dockWidgets = findChildren(); - - setTabPosition(Qt::TopDockWidgetArea, QTabWidget::TabPosition::North); QMainWindow::addDockWidget(area, dockWidget, orientation); - // We want to force tabbing, so retabify all of our widgets. QDockWidget* lastDockWidget = dockWidget; foreach(QDockWidget* nextDockWidget, dockWidgets) { diff --git a/interface/src/ui/ToolWindow.h b/interface/src/ui/ToolWindow.h index 03ae85a418..43ab1ac2ce 100644 --- a/interface/src/ui/ToolWindow.h +++ b/interface/src/ui/ToolWindow.h @@ -7,7 +7,7 @@ // // 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_ToolWindow_h #define hifi_ToolWindow_h From 0063f9ae1d9e10fce4ef4e25fe5e9cf477c42340 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 15:16:54 -0700 Subject: [PATCH 0127/1003] 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 0128/1003] 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 cfca0fe8c02bbd06de7dce7965675dd368bbaabc Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 15:58:10 -0700 Subject: [PATCH 0129/1003] Adding a color indicator to whiteboard --- .../painting/whiteboard/whiteboardSpawner.js | 46 +++++++++++++------ .../entities/src/EntityScriptingInterface.cpp | 15 ++---- libraries/entities/src/EntityTreeElement.cpp | 13 +----- libraries/octree/src/Octree.cpp | 2 +- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 8dc8be1cda..6a92be3295 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -16,16 +16,10 @@ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); - var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); center.y += 0.4; -var whiteboardDimensions = { - x: 2, - y: 1.5, - z: 0.01 -}; var colors = [ hexToRgb("#2F8E84"), @@ -36,8 +30,16 @@ var colors = [ hexToRgb("#993369"), hexToRgb("#9B47C2") ]; + +//WHITEBOARD +var whiteboardDimensions = { + x: 2, + y: 1.5, + z: 0.02 +}; var whiteboard = Entities.addEntity({ - type: "Box", + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx", name: "whiteboard", position: center, rotation: rotation, @@ -53,19 +55,34 @@ var whiteboard = Entities.addEntity({ }) }); -//COLORS +// COLOR INDICATOR BOX +var colorIndicatorDimensions = { + x: whiteboardDimensions.x, + y: 0.2, + z: 0.02 +}; +scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); +var colorIndicatorPosition = Vec3.sum(center, {x: 0, y: whiteboardDimensions.y/2, z: 0}); +var colorIndicatorBox = Entities.addEntity({ + type: "Box", + color: colors[0], + rotation: rotation, + position: colorIndicatorPosition, + dimensions: colorIndicatorDimensions, + script: scriptURL +}); + +//COLOR BOXES var direction = Quat.getRight(rotation); var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); - var colorBoxes = []; - var colorSquareDimensions = { x: (whiteboardDimensions.x / 2) / (colors.length - 1), y: 0.1, z: 0.05 }; -colorBoxPosition.y += whiteboardDimensions.y / 2 + colorSquareDimensions.y / 2 - 0.01; +colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y/2 + colorSquareDimensions.y / 2 - 0.01; var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { @@ -77,13 +94,16 @@ for (var i = 0; i < colors.length; i++) { color: colors[i], script: scriptURL, userData: JSON.stringify({ - whiteboard: whiteboard + whiteboard: whiteboard, + colorIndicator: colorIndicatorBox }) }); colorBoxes.push(colorBox); colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); } + +// BLACK BOX var blackBoxDimensions = {x: .2, y: .2, z: 0.05}; colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x/2 - 0.01)); colorBoxPosition.y += 0.3; @@ -146,4 +166,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 692ca7a638..3a530c0f3a 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -416,20 +416,15 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra QString faceName = object.property("face").toVariant().toString(); if (faceName == "MIN_X_FACE") { value.face = MIN_X_FACE; - } - else if (faceName == "MAX_X_FACE") { + } else if (faceName == "MAX_X_FACE") { value.face = MAX_X_FACE; - } - else if (faceName == "MIN_Y_FACE") { + } else if (faceName == "MIN_Y_FACE") { value.face = MIN_Y_FACE; - } - else if (faceName == "MAX_Y_FACE") { + } else if (faceName == "MAX_Y_FACE") { value.face = MAX_Y_FACE; - } - else if (faceName == "MIN_Z_FACE") { + } else if (faceName == "MIN_Z_FACE") { value.face = MIN_Z_FACE; - } - else { + } else { value.face = MAX_Z_FACE; }; QScriptValue intersection = object.property("intersection"); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index b95b0ca673..56ab27836c 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -501,17 +501,8 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con int entityNumber = 0; bool somethingIntersected = false; forEachEntity([&](EntityItemPointer entity) { - if (entityIdsToInclude.size() > 0) { - bool entityInWhiteList = false; - // We only want to search whitelist if there is one, otherwise everything except blacklisted items are valid - for (int i = 0; i < entityIdsToInclude.size(); i++) { - if (entityIdsToInclude.at(i) == entity->getID()) { - entityInWhiteList = true; - } - } - if (!entityInWhiteList) { - return; - } + if (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) { + return; } AABox entityBox = entity->getAABox(); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3b6467401c..6f6f5af303 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -724,7 +724,7 @@ bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking}; + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, intersectedObject, false, precisionPicking}; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; From 4c709cba3551763195a6af37a3e6ce105b9cd09c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 16:17:57 -0700 Subject: [PATCH 0130/1003] Fixed color indicator position --- .../whiteboard/colorIndicatorEntityScript.js | 41 +++++++++++++++++++ .../whiteboard/whiteboardEntityScript.js | 4 +- .../painting/whiteboard/whiteboardSpawner.js | 9 ++-- 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 examples/painting/whiteboard/colorIndicatorEntityScript.js diff --git a/examples/painting/whiteboard/colorIndicatorEntityScript.js b/examples/painting/whiteboard/colorIndicatorEntityScript.js new file mode 100644 index 0000000000..97deb0bc67 --- /dev/null +++ b/examples/painting/whiteboard/colorIndicatorEntityScript.js @@ -0,0 +1,41 @@ +// +// colorIndicatorEntityScript.js +// examples/painting/whiteboard +// +// Created by Eric Levin on 9/21/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 +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ +/*global ColorIndicator */ + +(function() { + + var _this; + ColorIndicator = function() { + _this = this; + }; + + ColorIndicator.prototype = { + + changeColor: function() { + var newColor = Entities.getEntityProperties(this.whiteboard, "userData").currentColor; + Entities.editEntity(this.EntityID, { + color: newColor + }); + }, + + preload: function(entityID) { + this.entityID = entityID; + var props = Entities.getEntityProperties(this.entityID, "userData"); + this.position = props.position; + this.whiteboard = JSON.parse(props.userData).whiteboard; + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new ColorIndicator(); +}); \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 0cfa12746a..f376c4f61c 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -210,7 +210,9 @@ this.painting = false; this.strokes = []; this.whitelist = [this.entityID]; - this.strokeColor = JSON.parse(props.userData).currentColor; + var userData = JSON.parse(props.userData); + this.strokeColor = userData.currentColor; + this.colorIndicator = userData.colorIndicator; this.laserPointer = Overlays.addOverlay("circle3d", { color: this.strokeColor, solid: true, diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 6a92be3295..2624fba9b2 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -63,7 +63,7 @@ var colorIndicatorDimensions = { z: 0.02 }; scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); -var colorIndicatorPosition = Vec3.sum(center, {x: 0, y: whiteboardDimensions.y/2, z: 0}); +var colorIndicatorPosition = Vec3.sum(center, {x: 0, y: whiteboardDimensions.y/2 + colorIndicatorDimensions.y/2, z: 0}); var colorIndicatorBox = Entities.addEntity({ type: "Box", color: colors[0], @@ -73,6 +73,8 @@ var colorIndicatorBox = Entities.addEntity({ script: scriptURL }); +Entities.editEntity(whiteboard, {userData: JSON.stringify({currentColor: colors[i], colorIndicator: colorIndicatorBox})} ); + //COLOR BOXES var direction = Quat.getRight(rotation); var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); @@ -82,7 +84,7 @@ var colorSquareDimensions = { y: 0.1, z: 0.05 }; -colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y/2 + colorSquareDimensions.y / 2 - 0.01; +colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2; var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { @@ -117,7 +119,7 @@ var blackBox = Entities.addEntity({ userData: JSON.stringify({ whiteboard: whiteboard }) -}) +}); var eraseBoxDimensions = { @@ -159,6 +161,7 @@ function cleanup() { Entities.deleteEntity(whiteboard); Entities.deleteEntity(eraseAllText); Entities.deleteEntity(blackBox); + Entities.deleteEntity(colorIndicatorBox); colorBoxes.forEach(function(colorBox) { Entities.deleteEntity(colorBox); }); From 5082ac458316b6d0d2b548886459e35015db9605 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 16:46:23 -0700 Subject: [PATCH 0131/1003] Added color status bar --- .../whiteboard/colorIndicatorEntityScript.js | 9 ++--- .../whiteboard/whiteboardEntityScript.js | 2 ++ .../painting/whiteboard/whiteboardSpawner.js | 36 ++++++++++++++----- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/examples/painting/whiteboard/colorIndicatorEntityScript.js b/examples/painting/whiteboard/colorIndicatorEntityScript.js index 97deb0bc67..326e9ab9c3 100644 --- a/examples/painting/whiteboard/colorIndicatorEntityScript.js +++ b/examples/painting/whiteboard/colorIndicatorEntityScript.js @@ -21,20 +21,21 @@ ColorIndicator.prototype = { changeColor: function() { - var newColor = Entities.getEntityProperties(this.whiteboard, "userData").currentColor; - Entities.editEntity(this.EntityID, { + var userData = JSON.parse(Entities.getEntityProperties(this.whiteboard, "userData").userData); + var newColor = userData.currentColor; + Entities.editEntity(this.entityID, { color: newColor }); }, preload: function(entityID) { this.entityID = entityID; - var props = Entities.getEntityProperties(this.entityID, "userData"); + var props = Entities.getEntityProperties(this.entityID, ["position", "userData"]); this.position = props.position; this.whiteboard = JSON.parse(props.userData).whiteboard; }, - }; + }; // entity scripts always need to return a newly constructed object of our type return new ColorIndicator(); diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index f376c4f61c..9ed5d21065 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -189,6 +189,8 @@ Overlays.editOverlay(this.laserPointer, { color: this.strokeColor }); + + Entities.callEntityMethod(this.colorIndicator, "changeColor"); }, eraseBoard: function() { diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 2624fba9b2..66be92466d 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -63,17 +63,29 @@ var colorIndicatorDimensions = { z: 0.02 }; scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); -var colorIndicatorPosition = Vec3.sum(center, {x: 0, y: whiteboardDimensions.y/2 + colorIndicatorDimensions.y/2, z: 0}); +var colorIndicatorPosition = Vec3.sum(center, { + x: 0, + y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2, + z: 0 +}); var colorIndicatorBox = Entities.addEntity({ type: "Box", color: colors[0], rotation: rotation, position: colorIndicatorPosition, dimensions: colorIndicatorDimensions, - script: scriptURL + script: scriptURL, + userData: JSON.stringify({ + whiteboard: whiteboard + }) }); -Entities.editEntity(whiteboard, {userData: JSON.stringify({currentColor: colors[i], colorIndicator: colorIndicatorBox})} ); +Entities.editEntity(whiteboard, { + userData: JSON.stringify({ + currentColor: colors[0], + colorIndicator: colorIndicatorBox + }) +}); //COLOR BOXES var direction = Quat.getRight(rotation); @@ -84,7 +96,7 @@ var colorSquareDimensions = { y: 0.1, z: 0.05 }; -colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2; +colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2; var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { @@ -106,18 +118,26 @@ for (var i = 0; i < colors.length; i++) { // BLACK BOX -var blackBoxDimensions = {x: .2, y: .2, z: 0.05}; -colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x/2 - 0.01)); +var blackBoxDimensions = { + x: .2, + y: .2, + z: 0.05 +}; +colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x / 2 - 0.01)); colorBoxPosition.y += 0.3; var blackBox = Entities.addEntity({ type: 'Box', position: colorBoxPosition, dimensions: blackBoxDimensions, rotation: rotation, - color: {red: 0, green: 0, blue: 0}, + color: { + red: 0, + green: 0, + blue: 0 + }, script: scriptURL, userData: JSON.stringify({ - whiteboard: whiteboard + whiteboard: whiteboard }) }); From e39219c2b5a7e2d52b4de105d736c90015998255 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Oct 2015 16:51:52 -0700 Subject: [PATCH 0132/1003] 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 0133/1003] 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 0134/1003] 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 87439a049e90ef3792f4bc3d9076458e9bbcf7ce Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 14 Oct 2015 17:22:46 -0700 Subject: [PATCH 0135/1003] Made color indicator thinner --- examples/painting/whiteboard/whiteboardSpawner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 66be92466d..4219ee30bc 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -35,7 +35,7 @@ var colors = [ var whiteboardDimensions = { x: 2, y: 1.5, - z: 0.02 + z: 0.08 }; var whiteboard = Entities.addEntity({ type: "Model", @@ -59,7 +59,7 @@ var whiteboard = Entities.addEntity({ // COLOR INDICATOR BOX var colorIndicatorDimensions = { x: whiteboardDimensions.x, - y: 0.2, + y: 0.05, z: 0.02 }; scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); From a1e2292e62e8daf155ff2711ca457f1e80e6dcb7 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 17:27:45 -0700 Subject: [PATCH 0136/1003] remove title bar from docked tool widgets --- interface/src/scripting/WebWindowClass.cpp | 1 + interface/src/ui/ToolWindow.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 7e998ed8ff..51f0448897 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -45,6 +45,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid auto dockWidget = new QDockWidget(title, toolWindow); dockWidget->setFeatures(QDockWidget::DockWidgetMovable); + dockWidget->setTitleBarWidget(0); connect(dockWidget, &QDockWidget::visibilityChanged, this, &WebWindowClass::visibilityChanged); _webView = new QWebView(dockWidget); diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index de4b3887db..f5dcf279c9 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -39,11 +39,8 @@ bool ToolWindow::event(QEvent* event) { QMainWindow* mainWindow = qApp->getWindow(); QRect mainGeometry = mainWindow->geometry(); - int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int topMargin = titleBarHeight; - - _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, - DEFAULT_WIDTH, mainGeometry.height() - topMargin); + _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y(), + DEFAULT_WIDTH, mainGeometry.height()); } setGeometry(_lastGeometry); return true; @@ -124,9 +121,12 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, QList dockWidgets = findChildren(); QMainWindow::addDockWidget(area, dockWidget, orientation); - + + QDockWidget* lastDockWidget = dockWidget; + lastDockWidget->setTitleBarWidget(new QWidget()); + foreach(QDockWidget* nextDockWidget, dockWidgets) { tabifyDockWidget(lastDockWidget, nextDockWidget); lastDockWidget = nextDockWidget; From c18aeef6b39a6b2c83a2813056876c851bfd96a6 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 17:29:38 -0700 Subject: [PATCH 0137/1003] remove redundant call --- interface/src/scripting/WebWindowClass.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 51f0448897..7e998ed8ff 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -45,7 +45,6 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid auto dockWidget = new QDockWidget(title, toolWindow); dockWidget->setFeatures(QDockWidget::DockWidgetMovable); - dockWidget->setTitleBarWidget(0); connect(dockWidget, &QDockWidget::visibilityChanged, this, &WebWindowClass::visibilityChanged); _webView = new QWebView(dockWidget); From 12e103c90c03c707c9c13601166e06ee99d17fb2 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 14 Oct 2015 17:41:39 -0700 Subject: [PATCH 0138/1003] 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 0139/1003] 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 84973c590d2e5f9f7eb221c40f065782e91b0d29 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 21:17:19 -0700 Subject: [PATCH 0140/1003] Update ToolWindow.h --- interface/src/ui/ToolWindow.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/ToolWindow.h b/interface/src/ui/ToolWindow.h index 43ab1ac2ce..03ae85a418 100644 --- a/interface/src/ui/ToolWindow.h +++ b/interface/src/ui/ToolWindow.h @@ -7,7 +7,7 @@ // // 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_ToolWindow_h #define hifi_ToolWindow_h From c8b6fed0e041de9e59192543a423e990f8614d19 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 14 Oct 2015 21:18:17 -0700 Subject: [PATCH 0141/1003] Update ToolWindow.cpp --- interface/src/ui/ToolWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index f5dcf279c9..c7de8b7424 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -122,7 +122,6 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, QMainWindow::addDockWidget(area, dockWidget, orientation); - QDockWidget* lastDockWidget = dockWidget; lastDockWidget->setTitleBarWidget(new QWidget()); From c99573ea64a812c417a1dc2a7a3d3f786eed653b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 15 Oct 2015 15:35:35 +0200 Subject: [PATCH 0142/1003] Fixed support for calling setJointMumblers with joint name. Walk.js runs smooth now! --- examples/libraries/walkApi.js | 10 +++++----- examples/walk.js | 2 +- libraries/avatars/src/AvatarData.cpp | 21 +++++++++++++++++++-- libraries/avatars/src/AvatarData.h | 2 ++ 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 5b42a8fb78..d1192deee7 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -82,7 +82,7 @@ Avatar = function() { // only need to zero right leg IK chain and hips if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(joint), Quat.fromPitchYawRollDegrees(0, 0, 0)); + MyAvatar.setJointRotation(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); } } this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; @@ -112,16 +112,16 @@ Avatar = function() { this.poseFingers = function() { for (knuckle in walkAssets.animationReference.leftHand) { if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, -4)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); } else { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, 5)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); } } for (knuckle in walkAssets.animationReference.rightHand) { if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(0, 0, 4)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); } else { - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(knuckle), Quat.fromPitchYawRollDegrees(16, 0, -5)); + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); } } }; diff --git a/examples/walk.js b/examples/walk.js index 02bc23a2e2..0b5bcab65a 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -449,6 +449,6 @@ function renderMotion() { } // apply rotations - MyAvatar.setJointRotation(MyAvatar.jointNames.indexOf(jointName), Quat.fromVec3Degrees(jointRotations)); + MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations)); } } \ No newline at end of file diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a9ff9541ea..a698c6f374 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1032,13 +1032,30 @@ glm::vec3 AvatarData::getJointTranslation(const QString& name) const { void AvatarData::setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), - Q_ARG(const glm::quat&, rotation)); + QMetaObject::invokeMethod(this, "setJointData", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation), + Q_ARG(const glm::vec3&, translation)); return; } setJointData(getJointIndex(name), rotation, translation); } +void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(const QString&, name), Q_ARG(const glm::quat&, rotation)); + return; + } + setJointRotation(getJointIndex(name), rotation); +} + +void AvatarData::setJointTranslation(const QString& name, const glm::vec3& translation) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(const QString&, name), + Q_ARG(const glm::vec3&, translation)); + return; + } + setJointTranslation(getJointIndex(name), translation); +} + void AvatarData::setJointRotation(int index, const glm::quat& rotation) { if (index == -1) { return; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e4022fd474..3abd63bf63 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -251,6 +251,8 @@ public: Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; Q_INVOKABLE void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); + Q_INVOKABLE void setJointRotation(const QString& name, const glm::quat& rotation); + Q_INVOKABLE void setJointTranslation(const QString& name, const glm::vec3& translation); Q_INVOKABLE void clearJointData(const QString& name); Q_INVOKABLE bool isJointDataValid(const QString& name) const; Q_INVOKABLE glm::quat getJointRotation(const QString& name) const; From 5ebefadda1e4ffb97dd3459599e41b9c137004ea Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 09:07:35 -0700 Subject: [PATCH 0143/1003] remove some debugging --- interface/src/Application.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 733858dd7e..e0e5003830 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2054,42 +2054,11 @@ void Application::checkFPS() { _timerStart.start(); } -uint64_t lastReport = usecTimestampNow(); // XXX - - void Application::idle() { if (_aboutToQuit) { return; // bail early, nothing to do here. } - - // XXX - { // XXX - uint64_t now = usecTimestampNow(); - if (now - lastReport > 6 * USECS_PER_SECOND) { - QSharedPointer avatarManager = DependencyManager::get(); - const AvatarHash& avatarHash = avatarManager->getAvatarHash(); - - qDebug() << "-----------------------"; - - qDebug() << "my left palm:" << getMyAvatar()->getLeftPalmPosition() - << " my chest:" << getMyAvatar()->getChestPosition(); - - QHash::const_iterator i; - for (i = avatarHash.begin(); i != avatarHash.end(); ++i) { - std::shared_ptr otherAvatar = std::static_pointer_cast(i.value()); - if (otherAvatar->getSessionUUID() == getMyAvatar()->getSessionUUID()) { - continue; - } - qDebug() << "their left palm:" << otherAvatar->getLeftPalmPosition() - << " their chest:" << otherAvatar->getChestPosition(); - }; - - lastReport = now; - } - } // XXX - // XXX - // depending on whether we're throttling or not. // Once rendering is off on another thread we should be able to have Application::idle run at start(0) in // perpetuity and not expect events to get backed up. From 9f40c7bf8cedca0a36d3d866f4213217d17e315e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 09:13:06 -0700 Subject: [PATCH 0144/1003] fix up AvatarActionHold --- interface/src/avatar/AvatarActionHold.cpp | 205 +++++++--------------- 1 file changed, 65 insertions(+), 140 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index f12909e809..22585d962a 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -21,8 +21,7 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit ObjectActionSpring(id, ownerEntity), _relativePosition(glm::vec3(0.0f)), _relativeRotation(glm::quat()), - _hand("right"), - _holderID(QUuid()) + _hand("right") { _type = ACTION_TYPE_HOLD; #if WANT_DEBUG @@ -37,122 +36,100 @@ AvatarActionHold::~AvatarActionHold() { } void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - bool gotLock = false; glm::quat rotation; glm::vec3 position; - std::shared_ptr holdingAvatar = nullptr; - - gotLock = withTryReadLock([&]{ - QSharedPointer avatarManager = DependencyManager::get(); - AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); - holdingAvatar = std::static_pointer_cast(holdingAvatarData); - - if (holdingAvatar) { - glm::vec3 offset; - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = holdingAvatar->getRightPalmPosition(); - palmRotation = holdingAvatar->getRightPalmRotation(); - } else { - palmPosition = holdingAvatar->getLeftPalmPosition(); - palmRotation = holdingAvatar->getLeftPalmRotation(); - } - - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; + glm::vec3 offset; + bool gotLock = withTryReadLock([&]{ + auto myAvatar = DependencyManager::get()->getMyAvatar(); + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = myAvatar->getRightPalmPosition(); + palmRotation = myAvatar->getRightPalmRotation(); + } else { + palmPosition = myAvatar->getLeftPalmPosition(); + palmRotation = myAvatar->getLeftPalmRotation(); } + + rotation = palmRotation * _relativeRotation; + offset = rotation * _relativePosition; + position = palmPosition + offset; }); - if (holdingAvatar) { - if (gotLock) { - gotLock = withTryWriteLock([&]{ + if (gotLock) { + gotLock = withTryWriteLock([&]{ + if (_positionalTarget != position || _rotationalTarget != rotation) { + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } _positionalTarget = position; _rotationalTarget = rotation; - }); - } - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); - } + } + }); + } + + if (gotLock) { + ObjectActionSpring::updateActionWorker(deltaTimeStep); } } bool AvatarActionHold::updateArguments(QVariantMap arguments) { - glm::vec3 relativePosition; - glm::quat relativeRotation; - float timeScale; - QString hand; - QUuid holderID; - bool needUpdate = false; + if (!ObjectAction::updateArguments(arguments)) { + return false; + } + bool ok = true; + glm::vec3 relativePosition = + EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); + if (!ok) { + relativePosition = _relativePosition; + } - bool somethingChanged = ObjectAction::updateArguments(arguments); - withReadLock([&]{ - bool ok = true; - relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); - if (!ok) { - relativePosition = _relativePosition; - } + ok = true; + glm::quat relativeRotation = + EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); + if (!ok) { + relativeRotation = _relativeRotation; + } + + ok = true; + float timeScale = + EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); + if (!ok) { + timeScale = _linearTimeScale; + } - ok = true; - relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); - if (!ok) { - relativeRotation = _relativeRotation; - } + ok = true; + QString hand = + EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); + if (!ok || !(hand == "left" || hand == "right")) { + hand = _hand; + } - ok = true; - timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); - if (!ok) { - timeScale = _linearTimeScale; - } - - ok = true; - hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); - if (!ok || !(hand == "left" || hand == "right")) { - hand = _hand; - } - - ok = true; - auto myAvatar = DependencyManager::get()->getMyAvatar(); - holderID = myAvatar->getSessionUUID(); - - if (somethingChanged || - relativePosition != _relativePosition + if (relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale - || hand != _hand - || holderID != _holderID) { - needUpdate = true; - } - }); - - if (needUpdate) { + || hand != _hand) { withWriteLock([&] { _relativePosition = relativePosition; _relativeRotation = relativeRotation; const float MIN_TIMESCALE = 0.1f; - _linearTimeScale = glm::max(MIN_TIMESCALE, timeScale); + _linearTimeScale = glm::min(MIN_TIMESCALE, timeScale); _angularTimeScale = _linearTimeScale; _hand = hand; - _holderID = holderID; + _active = true; - - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - } + activateBody(); }); - activateBody(); } - return true; } + QVariantMap AvatarActionHold::getArguments() { QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&]{ - arguments["holderID"] = _holderID; arguments["relativePosition"] = glmToQMap(_relativePosition); arguments["relativeRotation"] = glmToQMap(_relativeRotation); arguments["timeScale"] = _linearTimeScale; @@ -162,61 +139,9 @@ QVariantMap AvatarActionHold::getArguments() { } QByteArray AvatarActionHold::serialize() const { - QByteArray serializedActionArguments; - QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); - - withReadLock([&]{ - dataStream << ACTION_TYPE_HOLD; - dataStream << getID(); - dataStream << AvatarActionHold::holdVersion; - - dataStream << _holderID; - dataStream << _relativePosition; - dataStream << _relativeRotation; - dataStream << _linearTimeScale; - dataStream << _hand; - - dataStream << _expires; - dataStream << _tag; - }); - - return serializedActionArguments; + return ObjectActionSpring::serialize(); } void AvatarActionHold::deserialize(QByteArray serializedArguments) { - QDataStream dataStream(serializedArguments); - - EntityActionType type; - dataStream >> type; - assert(type == getType()); - - QUuid id; - dataStream >> id; - assert(id == getID()); - - uint16_t serializationVersion; - dataStream >> serializationVersion; - if (serializationVersion != AvatarActionHold::holdVersion) { - return; - } - - withWriteLock([&]{ - dataStream >> _holderID; - dataStream >> _relativePosition; - dataStream >> _relativeRotation; - dataStream >> _linearTimeScale; - _angularTimeScale = _linearTimeScale; - dataStream >> _hand; - - dataStream >> _expires; - dataStream >> _tag; - - #if WANT_DEBUG - qDebug() << "deserialize AvatarActionHold: " << _holderID - << _relativePosition.x << _relativePosition.y << _relativePosition.z - << _hand << _expires; - #endif - - _active = true; - }); + assert(false); } From 7ef5fc9613cf190cb03482b8dad8391b675535ef Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 09:19:47 -0700 Subject: [PATCH 0145/1003] remove line that leaked over from other branch --- interface/src/avatar/AvatarActionHold.h | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 3cc70ced09..2db23e10dc 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -38,7 +38,6 @@ private: glm::vec3 _relativePosition; glm::quat _relativeRotation; QString _hand; - QUuid _holderID; }; #endif // hifi_AvatarActionHold_h From fa17b77d70a565265e92611da0f887372a03cc25 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 15 Oct 2015 09:06:31 -0700 Subject: [PATCH 0146/1003] move DIRTY_FLAGS out of EntityItem namespace --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 64 +++++++++---------- libraries/entities/src/EntityItem.h | 21 +----- libraries/entities/src/EntitySimulation.cpp | 6 +- libraries/entities/src/EntitySimulation.h | 24 +++---- libraries/entities/src/ModelEntityItem.cpp | 16 ++--- .../entities/src/ParticleEffectEntityItem.cpp | 2 +- libraries/entities/src/SimulationFlags.h | 34 ++++++++++ libraries/physics/src/EntityMotionState.cpp | 10 +-- libraries/physics/src/ObjectMotionState.cpp | 20 +++--- libraries/physics/src/ObjectMotionState.h | 14 ++-- 13 files changed, 117 insertions(+), 100 deletions(-) create mode 100644 libraries/entities/src/SimulationFlags.h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index c0636314b5..e0ace803ce 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -1034,7 +1034,7 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) { const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; if (_moving && _motionState) { - _motionState->addDirtyFlags(EntityItem::DIRTY_POSITION); + _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); } endUpdate(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 69d76db7de..60b4b1f1ab 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -309,7 +309,7 @@ void AvatarManager::updateAvatarPhysicsShape(const QUuid& id) { auto avatar = std::static_pointer_cast(avatarItr.value()); AvatarMotionState* motionState = avatar->getMotionState(); if (motionState) { - motionState->addDirtyFlags(EntityItem::DIRTY_SHAPE); + motionState->addDirtyFlags(Simulation::DIRTY_SHAPE); } else { ShapeInfo shapeInfo; avatar->computeShapeInfo(shapeInfo); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 5d9a247741..fd5a9a6b4a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1057,7 +1057,7 @@ void RenderablePolyVoxEntityItem::getMeshAsync() { gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); _meshLock.lockForWrite(); - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; _mesh = mesh; _meshDirty = true; _meshLock.unlock(); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a8f3adc12e..5bc48ecb28 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -630,7 +630,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bytesRead += bytes; if (_simulationOwner.set(newSimOwner)) { - _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } } { // When we own the simulation we don't accept updates to the entity's transform/velocities @@ -729,7 +729,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID); } - if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES))) { + if (overwriteLocalData && (getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES))) { // NOTE: This code is attempting to "repair" the old data we just got from the server to make it more // closely match where the entities should be if they'd stepped forward in time to "now". The server // is sending us data with a known "last simulated" time. That time is likely in the past, and therefore @@ -813,7 +813,7 @@ void EntityItem::updateDensity(float density) { if (fabsf(_density - clampedDensity) / _density > ACTIVATION_RELATIVE_DENSITY_DELTA) { // the density has changed enough that we should update the physics simulation - _dirtyFlags |= EntityItem::DIRTY_MASS; + _dirtyFlags |= Simulation::DIRTY_MASS; } } } @@ -905,7 +905,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { const float EPSILON_ANGULAR_VELOCITY_LENGTH = 0.0017453f; // 0.0017453 rad/sec = 0.1f degrees/sec if (angularSpeed < EPSILON_ANGULAR_VELOCITY_LENGTH) { if (setFlags && angularSpeed > 0.0f) { - _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } _angularVelocity = ENTITY_ITEM_ZERO_VEC3; } else { @@ -967,7 +967,7 @@ void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { if (speed < EPSILON_LINEAR_VELOCITY_LENGTH) { setVelocity(ENTITY_ITEM_ZERO_VEC3); if (setFlags && speed > 0.0f) { - _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } } else { setPosition(position); @@ -1134,7 +1134,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { #endif setLastEdited(now); somethingChangedNotification(); // notify derived classes that something has changed - if (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES)) { + if (getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) { // anything that sets the transform or velocity must update _lastSimulated which is used // for kinematic extrapolation (e.g. we want to extrapolate forward from this moment // when position and/or velocity was changed). @@ -1302,10 +1302,10 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) { void EntityItem::updatePosition(const glm::vec3& value) { auto delta = glm::distance(getPosition(), value); if (delta > IGNORE_POSITION_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_POSITION; + _dirtyFlags |= Simulation::DIRTY_POSITION; setPosition(value); if (delta > ACTIVATION_POSITION_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } } } @@ -1316,7 +1316,7 @@ void EntityItem::updateDimensions(const glm::vec3& value) { setDimensions(value); if (delta > ACTIVATION_DIMENSIONS_DELTA) { // rebuilding the shape will always activate - _dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS); + _dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); } } } @@ -1327,10 +1327,10 @@ void EntityItem::updateRotation(const glm::quat& rotation) { auto alignmentDot = glm::abs(glm::dot(getRotation(), rotation)); if (alignmentDot < IGNORE_ALIGNMENT_DOT) { - _dirtyFlags |= EntityItem::DIRTY_ROTATION; + _dirtyFlags |= Simulation::DIRTY_ROTATION; } if (alignmentDot < ACTIVATION_ALIGNMENT_DOT) { - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } } } @@ -1357,14 +1357,14 @@ void EntityItem::updateMass(float mass) { if (fabsf(_density - oldDensity) / _density > ACTIVATION_RELATIVE_DENSITY_DELTA) { // the density has changed enough that we should update the physics simulation - _dirtyFlags |= EntityItem::DIRTY_MASS; + _dirtyFlags |= Simulation::DIRTY_MASS; } } void EntityItem::updateVelocity(const glm::vec3& value) { auto delta = glm::distance(_velocity, value); if (delta > IGNORE_LINEAR_VELOCITY_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_LINEAR_VELOCITY; + _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; const float MIN_LINEAR_SPEED = 0.001f; if (glm::length(value) < MIN_LINEAR_SPEED) { _velocity = ENTITY_ITEM_ZERO_VEC3; @@ -1372,7 +1372,7 @@ void EntityItem::updateVelocity(const glm::vec3& value) { _velocity = value; // only activate when setting non-zero velocity if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } } } @@ -1382,7 +1382,7 @@ void EntityItem::updateDamping(float value) { auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); if (fabsf(_damping - clampedDamping) > IGNORE_DAMPING_DELTA) { _damping = clampedDamping; - _dirtyFlags |= EntityItem::DIRTY_MATERIAL; + _dirtyFlags |= Simulation::DIRTY_MATERIAL; } } @@ -1390,9 +1390,9 @@ void EntityItem::updateGravity(const glm::vec3& value) { auto delta = glm::distance(_gravity, value); if (delta > IGNORE_GRAVITY_DELTA) { _gravity = value; - _dirtyFlags |= EntityItem::DIRTY_LINEAR_VELOCITY; + _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; if (delta > ACTIVATION_GRAVITY_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } } } @@ -1400,7 +1400,7 @@ void EntityItem::updateGravity(const glm::vec3& value) { void EntityItem::updateAngularVelocity(const glm::vec3& value) { auto delta = glm::distance(_angularVelocity, value); if (delta > IGNORE_ANGULAR_VELOCITY_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_ANGULAR_VELOCITY; + _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY; const float MIN_ANGULAR_SPEED = 0.0002f; if (glm::length(value) < MIN_ANGULAR_SPEED) { _angularVelocity = ENTITY_ITEM_ZERO_VEC3; @@ -1408,7 +1408,7 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) { _angularVelocity = value; // only activate when setting non-zero velocity if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) { - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } } } @@ -1418,21 +1418,21 @@ void EntityItem::updateAngularDamping(float value) { auto clampedDamping = glm::clamp(value, 0.0f, 1.0f); if (fabsf(_angularDamping - clampedDamping) > IGNORE_DAMPING_DELTA) { _angularDamping = clampedDamping; - _dirtyFlags |= EntityItem::DIRTY_MATERIAL; + _dirtyFlags |= Simulation::DIRTY_MATERIAL; } } void EntityItem::updateIgnoreForCollisions(bool value) { if (_ignoreForCollisions != value) { _ignoreForCollisions = value; - _dirtyFlags |= EntityItem::DIRTY_COLLISION_GROUP; + _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; } } void EntityItem::updateCollisionsWillMove(bool value) { if (_collisionsWillMove != value) { _collisionsWillMove = value; - _dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } } @@ -1440,7 +1440,7 @@ void EntityItem::updateRestitution(float value) { float clampedValue = glm::max(glm::min(ENTITY_ITEM_MAX_RESTITUTION, value), ENTITY_ITEM_MIN_RESTITUTION); if (_restitution != clampedValue) { _restitution = clampedValue; - _dirtyFlags |= EntityItem::DIRTY_MATERIAL; + _dirtyFlags |= Simulation::DIRTY_MATERIAL; } } @@ -1448,7 +1448,7 @@ void EntityItem::updateFriction(float value) { float clampedValue = glm::max(glm::min(ENTITY_ITEM_MAX_FRICTION, value), ENTITY_ITEM_MIN_FRICTION); if (_friction != clampedValue) { _friction = clampedValue; - _dirtyFlags |= EntityItem::DIRTY_MATERIAL; + _dirtyFlags |= Simulation::DIRTY_MATERIAL; } } @@ -1465,14 +1465,14 @@ void EntityItem::setFriction(float value) { void EntityItem::updateLifetime(float value) { if (_lifetime != value) { _lifetime = value; - _dirtyFlags |= EntityItem::DIRTY_LIFETIME; + _dirtyFlags |= Simulation::DIRTY_LIFETIME; } } void EntityItem::updateCreated(uint64_t value) { if (_created != value) { _created = value; - _dirtyFlags |= EntityItem::DIRTY_LIFETIME; + _dirtyFlags |= Simulation::DIRTY_LIFETIME; } } @@ -1486,7 +1486,7 @@ void EntityItem::setSimulationOwner(const SimulationOwner& owner) { void EntityItem::updateSimulatorID(const QUuid& value) { if (_simulationOwner.setID(value)) { - _dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } } @@ -1494,7 +1494,7 @@ void EntityItem::clearSimulationOwnership() { _simulationOwner.clear(); // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership() // is only ever called entity-server-side and the flags are only used client-side - //_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID; + //_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; } @@ -1533,7 +1533,7 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi serializeActions(success, newDataCache); if (success) { _allActionsDataCache = newDataCache; - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } return success; } @@ -1552,7 +1552,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI success = action->updateArguments(arguments); if (success) { serializeActions(success, _allActionsDataCache); - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } else { qDebug() << "EntityItem::updateAction failed"; } @@ -1588,7 +1588,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s bool success = true; serializeActions(success, _allActionsDataCache); - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; return success; } return false; @@ -1607,7 +1607,7 @@ bool EntityItem::clearActions(EntitySimulation* simulation) { // empty _serializedActions means no actions for the EntityItem _actionsToRemove.clear(); _allActionsDataCache.clear(); - _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; + _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; }); return true; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index cddd9df312..b8e33c28f8 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -29,6 +29,7 @@ #include "EntityPropertyFlags.h" #include "EntityTypes.h" #include "SimulationOwner.h" +#include "SimulationFlags.h" class EntitySimulation; class EntityTreeElement; @@ -102,24 +103,6 @@ class EntityItem : public std::enable_shared_from_this, public ReadW friend class EntityTreeElement; friend class EntitySimulation; public: - enum EntityDirtyFlags { - DIRTY_POSITION = 0x0001, - DIRTY_ROTATION = 0x0002, - DIRTY_LINEAR_VELOCITY = 0x0004, - DIRTY_ANGULAR_VELOCITY = 0x0008, - DIRTY_MASS = 0x0010, - DIRTY_COLLISION_GROUP = 0x0020, - DIRTY_MOTION_TYPE = 0x0040, - DIRTY_SHAPE = 0x0080, - DIRTY_LIFETIME = 0x0100, - DIRTY_UPDATEABLE = 0x0200, - DIRTY_MATERIAL = 0x00400, - DIRTY_PHYSICS_ACTIVATION = 0x0800, // should activate object in physics engine - DIRTY_SIMULATOR_OWNERSHIP = 0x1000, // should claim simulator ownership - DIRTY_SIMULATOR_ID = 0x2000, // the simulatorID has changed - DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION, - DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY - }; DONT_ALLOW_INSTANTIATION // This class can not be instantiated directly @@ -411,7 +394,7 @@ public: void getAllTerseUpdateProperties(EntityItemProperties& properties) const; - void flagForOwnership() { _dirtyFlags |= DIRTY_SIMULATOR_OWNERSHIP; } + void flagForOwnership() { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_OWNERSHIP; } bool addAction(EntitySimulation* simulation, EntityActionPointer action); bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments); diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index ceafaef28d..70b973e672 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -198,7 +198,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { // we must check for that case here, however we rely on the change event to have set DIRTY_POSITION flag. bool wasRemoved = false; uint32_t dirtyFlags = entity->getDirtyFlags(); - if (dirtyFlags & EntityItem::DIRTY_POSITION) { + if (dirtyFlags & Simulation::DIRTY_POSITION) { AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE); AACube newCube = entity->getMaximumAACube(); if (!domainBounds.touches(newCube)) { @@ -214,7 +214,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { } } if (!wasRemoved) { - if (dirtyFlags & EntityItem::DIRTY_LIFETIME) { + if (dirtyFlags & Simulation::DIRTY_LIFETIME) { if (entity->isMortal()) { _mortalEntities.insert(entity); quint64 expiry = entity->getExpiry(); @@ -224,7 +224,7 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) { } else { _mortalEntities.remove(entity); } - entity->clearDirtyFlags(EntityItem::DIRTY_LIFETIME); + entity->clearDirtyFlags(Simulation::DIRTY_LIFETIME); } if (entity->needsToCallUpdate()) { _entitiesToUpdate.insert(entity); diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index c3d9be73ad..4519effbd3 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -28,18 +28,18 @@ typedef QVector VectorOfEntities; // the EntitySimulation needs to know when these things change on an entity, // so it can sort EntityItem or relay its state to the PhysicsEngine. const int DIRTY_SIMULATION_FLAGS = - EntityItem::DIRTY_POSITION | - EntityItem::DIRTY_ROTATION | - EntityItem::DIRTY_LINEAR_VELOCITY | - EntityItem::DIRTY_ANGULAR_VELOCITY | - EntityItem::DIRTY_MASS | - EntityItem::DIRTY_COLLISION_GROUP | - EntityItem::DIRTY_MOTION_TYPE | - EntityItem::DIRTY_SHAPE | - EntityItem::DIRTY_LIFETIME | - EntityItem::DIRTY_UPDATEABLE | - EntityItem::DIRTY_MATERIAL | - EntityItem::DIRTY_SIMULATOR_ID; + Simulation::DIRTY_POSITION | + Simulation::DIRTY_ROTATION | + Simulation::DIRTY_LINEAR_VELOCITY | + Simulation::DIRTY_ANGULAR_VELOCITY | + Simulation::DIRTY_MASS | + Simulation::DIRTY_COLLISION_GROUP | + Simulation::DIRTY_MOTION_TYPE | + Simulation::DIRTY_SHAPE | + Simulation::DIRTY_LIFETIME | + Simulation::DIRTY_UPDATEABLE | + Simulation::DIRTY_MATERIAL | + Simulation::DIRTY_SIMULATOR_ID; class EntitySimulation : public QObject { Q_OBJECT diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index eb2f1b0ebd..b4adde7467 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -66,7 +66,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChangedInAnimations = _animationProperties.setProperties(properties); if (somethingChangedInAnimations) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; } somethingChanged = somethingChanged || somethingChangedInAnimations; @@ -128,7 +128,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType); if (animationPropertiesChanged) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; somethingChanged = true; } @@ -300,7 +300,7 @@ void ModelEntityItem::updateShapeType(ShapeType type) { if (type != _shapeType) { _shapeType = type; - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } } @@ -316,13 +316,13 @@ ShapeType ModelEntityItem::getShapeType() const { void ModelEntityItem::setCompoundShapeURL(const QString& url) { if (_compoundShapeURL != url) { _compoundShapeURL = url; - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; _shapeType = _compoundShapeURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND; } } void ModelEntityItem::setAnimationURL(const QString& url) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; _animationProperties.setURL(url); } @@ -388,16 +388,16 @@ void ModelEntityItem::setAnimationSettings(const QString& value) { setAnimationStartAutomatically(startAutomatically); } - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; } void ModelEntityItem::setAnimationIsPlaying(bool value) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; _animationLoop.setRunning(value); } void ModelEntityItem::setAnimationFPS(float value) { - _dirtyFlags |= EntityItem::DIRTY_UPDATEABLE; + _dirtyFlags |= Simulation::DIRTY_UPDATEABLE; _animationLoop.setFPS(value); } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 31d34c743c..29e2aa512d 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -597,7 +597,7 @@ void ParticleEffectEntityItem::debugDump() const { void ParticleEffectEntityItem::updateShapeType(ShapeType type) { if (type != _shapeType) { _shapeType = type; - _dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS; + _dirtyFlags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS; } } diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h new file mode 100644 index 0000000000..91db978e1d --- /dev/null +++ b/libraries/entities/src/SimulationFlags.h @@ -0,0 +1,34 @@ +// +// SimulationFlags.h +// libraries/physics/src +// +// Created by Andrew Meadows 2015.10.14 +// 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_SimulationFlags_h +#define hifi_SimulationFlags_h + +namespace Simulation { + const uint32_t DIRTY_POSITION = 0x0001; + const uint32_t DIRTY_ROTATION = 0x0002; + const uint32_t DIRTY_LINEAR_VELOCITY = 0x0004; + const uint32_t DIRTY_ANGULAR_VELOCITY = 0x0008; + const uint32_t DIRTY_MASS = 0x0010; + const uint32_t DIRTY_COLLISION_GROUP = 0x0020; + const uint32_t DIRTY_MOTION_TYPE = 0x0040; + const uint32_t DIRTY_SHAPE = 0x0080; + const uint32_t DIRTY_LIFETIME = 0x0100; + const uint32_t DIRTY_UPDATEABLE = 0x0200; + const uint32_t DIRTY_MATERIAL = 0x00400; + const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine + const uint32_t DIRTY_SIMULATOR_OWNERSHIP = 0x1000; // should claim simulator ownership + const uint32_t DIRTY_SIMULATOR_ID = 0x2000; // the simulatorID has changed + const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; + const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; +}; + +#endif // hifi_SimulationFlags_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 42bf9bd438..b37441c15b 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -93,13 +93,13 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) updateServerPhysicsVariables(); ObjectMotionState::handleEasyChanges(flags, engine); - if (flags & EntityItem::DIRTY_SIMULATOR_ID) { + if (flags & Simulation::DIRTY_SIMULATOR_ID) { _loopsWithoutOwner = 0; if (_entity->getSimulatorID().isNull()) { // simulation ownership is being removed // remove the ACTIVATION flag because this object is coming to rest // according to a remote simulation and we don't want to wake it up again - flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION; + flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION; // hint to Bullet that the object is deactivating _body->setActivationState(WANTS_DEACTIVATION); _outgoingPriority = NO_PRORITY; @@ -111,13 +111,13 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) } } } - if (flags & EntityItem::DIRTY_SIMULATOR_OWNERSHIP) { + if (flags & Simulation::DIRTY_SIMULATOR_OWNERSHIP) { // (DIRTY_SIMULATOR_OWNERSHIP really means "we should bid for ownership with SCRIPT priority") // we're manipulating this object directly via script, so we artificially // manipulate the logic to trigger an immediate bid for ownership setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); } - if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { + if ((flags & Simulation::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) { _body->activate(); } @@ -503,7 +503,7 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() { bool isMoving = _entity->isMoving(); if (((bodyFlags & btCollisionObject::CF_STATIC_OBJECT) && isMoving) || (bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) { - dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE; + dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } } return dirtyFlags; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 85bddd0495..d2c152d876 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -126,34 +126,34 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) { } bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { - if (flags & EntityItem::DIRTY_POSITION) { + if (flags & Simulation::DIRTY_POSITION) { btTransform worldTrans; - if (flags & EntityItem::DIRTY_ROTATION) { + if (flags & Simulation::DIRTY_ROTATION) { worldTrans.setRotation(glmToBullet(getObjectRotation())); } else { worldTrans = _body->getWorldTransform(); } worldTrans.setOrigin(glmToBullet(getObjectPosition())); _body->setWorldTransform(worldTrans); - } else if (flags & EntityItem::DIRTY_ROTATION) { + } else if (flags & Simulation::DIRTY_ROTATION) { btTransform worldTrans = _body->getWorldTransform(); worldTrans.setRotation(glmToBullet(getObjectRotation())); _body->setWorldTransform(worldTrans); } - if (flags & EntityItem::DIRTY_LINEAR_VELOCITY) { + if (flags & Simulation::DIRTY_LINEAR_VELOCITY) { _body->setLinearVelocity(glmToBullet(getObjectLinearVelocity())); _body->setGravity(glmToBullet(getObjectGravity())); } - if (flags & EntityItem::DIRTY_ANGULAR_VELOCITY) { + if (flags & Simulation::DIRTY_ANGULAR_VELOCITY) { _body->setAngularVelocity(glmToBullet(getObjectAngularVelocity())); } - if (flags & EntityItem::DIRTY_MATERIAL) { + if (flags & Simulation::DIRTY_MATERIAL) { updateBodyMaterialProperties(); } - if (flags & EntityItem::DIRTY_MASS) { + if (flags & Simulation::DIRTY_MASS) { updateBodyMassProperties(); } @@ -161,7 +161,7 @@ bool ObjectMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) } bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { - if (flags & EntityItem::DIRTY_SHAPE) { + if (flags & Simulation::DIRTY_SHAPE) { // make sure the new shape is valid if (!isReadyToComputeShape()) { return false; @@ -170,7 +170,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* if (!newShape) { qCDebug(physics) << "Warning: failed to generate new shape!"; // failed to generate new shape! --> keep old shape and remove shape-change flag - flags &= ~EntityItem::DIRTY_SHAPE; + flags &= ~Simulation::DIRTY_SHAPE; // TODO: force this object out of PhysicsEngine rather than just use the old shape if ((flags & HARD_DIRTY_PHYSICS_FLAGS) == 0) { // no HARD flags remain, so do any EASY changes @@ -186,7 +186,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* _body->setCollisionShape(_shape); } else { // huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag - flags &= ~EntityItem::DIRTY_SHAPE; + flags &= ~Simulation::DIRTY_SHAPE; } } if (flags & EASY_DIRTY_PHYSICS_FLAGS) { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 61254e49bd..ec98f8858f 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -37,18 +37,18 @@ enum MotionStateType { // The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. -const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE | - EntityItem::DIRTY_COLLISION_GROUP); -const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES | - EntityItem::DIRTY_MASS | EntityItem::DIRTY_MATERIAL | - EntityItem::DIRTY_SIMULATOR_ID | EntityItem::DIRTY_SIMULATOR_OWNERSHIP); +const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | + Simulation::DIRTY_COLLISION_GROUP); +const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | + Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | + Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATOR_OWNERSHIP); // These are the set of incoming flags that the PhysicsEngine needs to hear about: const uint32_t DIRTY_PHYSICS_FLAGS = (uint32_t)(HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS | - EntityItem::DIRTY_PHYSICS_ACTIVATION); + Simulation::DIRTY_PHYSICS_ACTIVATION); // These are the outgoing flags that the PhysicsEngine can affect: -const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES; +const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES; class OctreeEditPacketSender; From d833fb08d74f365323822f5e0dd8553c42c70b90 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:03:32 -0700 Subject: [PATCH 0147/1003] further unmangle branch split --- interface/src/avatar/AvatarActionHold.cpp | 95 +++++++++++++---------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 22585d962a..1529805e1e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -36,10 +36,12 @@ AvatarActionHold::~AvatarActionHold() { } void AvatarActionHold::updateActionWorker(float deltaTimeStep) { + bool gotLock = false; glm::quat rotation; glm::vec3 position; glm::vec3 offset; - bool gotLock = withTryReadLock([&]{ + + gotLock = withTryReadLock([&]{ auto myAvatar = DependencyManager::get()->getMyAvatar(); glm::vec3 palmPosition; glm::quat palmRotation; @@ -58,17 +60,10 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { if (gotLock) { gotLock = withTryWriteLock([&]{ - if (_positionalTarget != position || _rotationalTarget != rotation) { - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - } _positionalTarget = position; _rotationalTarget = rotation; - } - }); + }); } - if (gotLock) { ObjectActionSpring::updateActionWorker(deltaTimeStep); } @@ -76,57 +71,73 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { bool AvatarActionHold::updateArguments(QVariantMap arguments) { - if (!ObjectAction::updateArguments(arguments)) { - return false; - } - bool ok = true; - glm::vec3 relativePosition = - EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); - if (!ok) { - relativePosition = _relativePosition; - } + glm::vec3 relativePosition; + glm::quat relativeRotation; + float timeScale; + QString hand; + QUuid holderID; + bool needUpdate = false; - ok = true; - glm::quat relativeRotation = - EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); - if (!ok) { - relativeRotation = _relativeRotation; - } - - ok = true; - float timeScale = - EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); - if (!ok) { - timeScale = _linearTimeScale; - } + bool somethingChanged = ObjectAction::updateArguments(arguments); + withReadLock([&]{ + bool ok = true; + relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); + if (!ok) { + relativePosition = _relativePosition; + } - ok = true; - QString hand = - EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); - if (!ok || !(hand == "left" || hand == "right")) { - hand = _hand; - } + ok = true; + relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false); + if (!ok) { + relativeRotation = _relativeRotation; + } - if (relativePosition != _relativePosition + ok = true; + timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false); + if (!ok) { + timeScale = _linearTimeScale; + } + + ok = true; + hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false); + if (!ok || !(hand == "left" || hand == "right")) { + hand = _hand; + } + + ok = true; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + holderID = myAvatar->getSessionUUID(); + + if (somethingChanged || + relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale || hand != _hand) { + needUpdate = true; + } + }); + + if (needUpdate) { withWriteLock([&] { _relativePosition = relativePosition; _relativeRotation = relativeRotation; const float MIN_TIMESCALE = 0.1f; - _linearTimeScale = glm::min(MIN_TIMESCALE, timeScale); + _linearTimeScale = glm::max(MIN_TIMESCALE, timeScale); _angularTimeScale = _linearTimeScale; _hand = hand; - _active = true; - activateBody(); + + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } }); + activateBody(); } + return true; } - QVariantMap AvatarActionHold::getArguments() { QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&]{ From d8c979b9d4e093d37b76296abafc3cda5a5d7a66 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 15 Oct 2015 10:07:21 -0700 Subject: [PATCH 0148/1003] add lifetime to lights created by flashlight --- examples/toys/flashlight/flashlight.js | 30 ++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/examples/toys/flashlight/flashlight.js b/examples/toys/flashlight/flashlight.js index 9836579afd..a775f185e3 100644 --- a/examples/toys/flashlight/flashlight.js +++ b/examples/toys/flashlight/flashlight.js @@ -22,6 +22,12 @@ var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav'; var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav'; + //we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them + var startTime = Date.now(); + //if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value. + var LIFETIME = 25; + var MSEC_PER_SEC = 1000.0; + // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) function Flashlight() { @@ -32,10 +38,22 @@ var DISABLE_LIGHT_THRESHOLD = 0.7; // These constants define the Spotlight position and orientation relative to the model - var MODEL_LIGHT_POSITION = { x: 0, y: -0.3, z: 0 }; - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }); + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); - var GLOW_LIGHT_POSITION = { x: 0, y: -0.1, z: 0}; + var GLOW_LIGHT_POSITION = { + x: 0, + y: -0.1, + z: 0 + }; // Evaluate the world light entity positions and orientations from the model ones function evalLightWorldTransform(modelPos, modelRot) { @@ -86,7 +104,8 @@ }, intensity: 2, exponent: 0.3, - cutoff: 20 + cutoff: 20, + lifetime: LIFETIME }); //this light creates the effect of a bulb at the end of the flashlight @@ -105,6 +124,7 @@ }, exponent: 0, cutoff: 90, // in degrees + lifetime: LIFETIME }); this.hasSpotlight = true; @@ -151,11 +171,13 @@ Entities.editEntity(this.spotlight, { position: lightTransform.p, rotation: lightTransform.q, + lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME }); Entities.editEntity(this.glowLight, { position: glowLightTransform.p, rotation: glowLightTransform.q, + lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME }); }, From 8195145dc787b952d3230f82f9b154b30b42d968 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:13:00 -0700 Subject: [PATCH 0149/1003] further unmangle branch split --- interface/src/avatar/AvatarActionHold.cpp | 8 ++++---- interface/src/avatar/AvatarActionHold.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 1529805e1e..51394798ab 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -109,10 +109,10 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { holderID = myAvatar->getSessionUUID(); if (somethingChanged || - relativePosition != _relativePosition - || relativeRotation != _relativeRotation - || timeScale != _linearTimeScale - || hand != _hand) { + relativePosition != _relativePosition || + relativeRotation != _relativeRotation || + timeScale != _linearTimeScale || + hand != _hand) { needUpdate = true; } }); diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 2db23e10dc..840ba41c28 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressLocationEdits() { return true; } + virtual bool shouldSuppressLocationEdits() { return false; } private: static const uint16_t holdVersion; From e571cb7ca44384121fc48e7868a411198ec770a3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:22:25 -0700 Subject: [PATCH 0150/1003] more lock adjustments --- libraries/physics/src/ObjectActionOffset.cpp | 35 ++++++++--------- libraries/physics/src/ObjectActionSpring.cpp | 40 ++++++++++---------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index 4930d44f3c..d01178dcc3 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -151,17 +151,18 @@ QVariantMap ObjectActionOffset::getArguments() { QByteArray ObjectActionOffset::serialize() const { QByteArray ba; QDataStream dataStream(&ba, QIODevice::WriteOnly); - dataStream << getType(); + dataStream << ACTION_TYPE_OFFSET; dataStream << getID(); dataStream << ObjectActionOffset::offsetVersion; - dataStream << _pointToOffsetFrom; - dataStream << _linearDistance; - dataStream << _linearTimeScale; - dataStream << _positionalTargetSet; - - dataStream << _expires; - dataStream << _tag; + withReadLock([&] { + dataStream << _pointToOffsetFrom; + dataStream << _linearDistance; + dataStream << _linearTimeScale; + dataStream << _positionalTargetSet; + dataStream << _expires; + dataStream << _tag; + }); return ba; } @@ -183,13 +184,13 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) { return; } - dataStream >> _pointToOffsetFrom; - dataStream >> _linearDistance; - dataStream >> _linearTimeScale; - dataStream >> _positionalTargetSet; - - dataStream >> _expires; - dataStream >> _tag; - - _active = true; + withWriteLock([&] { + dataStream >> _pointToOffsetFrom; + dataStream >> _linearDistance; + dataStream >> _linearTimeScale; + dataStream >> _positionalTargetSet; + dataStream >> _expires; + dataStream >> _tag; + _active = true; + }); } diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 242849db6f..38b4b71d26 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -191,16 +191,16 @@ QByteArray ObjectActionSpring::serialize() const { dataStream << getID(); dataStream << ObjectActionSpring::springVersion; - dataStream << _positionalTarget; - dataStream << _linearTimeScale; - dataStream << _positionalTargetSet; - - dataStream << _rotationalTarget; - dataStream << _angularTimeScale; - dataStream << _rotationalTargetSet; - - dataStream << _expires; - dataStream << _tag; + withReadLock([&] { + dataStream << _positionalTarget; + dataStream << _linearTimeScale; + dataStream << _positionalTargetSet; + dataStream << _rotationalTarget; + dataStream << _angularTimeScale; + dataStream << _rotationalTargetSet; + dataStream << _expires; + dataStream << _tag; + }); return serializedActionArguments; } @@ -222,16 +222,18 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { return; } - dataStream >> _positionalTarget; - dataStream >> _linearTimeScale; - dataStream >> _positionalTargetSet; + withWriteLock([&] { + dataStream >> _positionalTarget; + dataStream >> _linearTimeScale; + dataStream >> _positionalTargetSet; - dataStream >> _rotationalTarget; - dataStream >> _angularTimeScale; - dataStream >> _rotationalTargetSet; + dataStream >> _rotationalTarget; + dataStream >> _angularTimeScale; + dataStream >> _rotationalTargetSet; - dataStream >> _expires; - dataStream >> _tag; + dataStream >> _expires; + dataStream >> _tag; - _active = true; + _active = true; + }); } From a78728c96b0f447f9a13e74df3ae6101c8096d2e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:29:25 -0700 Subject: [PATCH 0151/1003] further unmangle branch split --- interface/src/avatar/AvatarActionHold.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 51394798ab..4288a354f5 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -60,9 +60,11 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { if (gotLock) { gotLock = withTryWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - }); + _positionalTarget = position; + _rotationalTarget = rotation; + _positionalTargetSet = true; + _rotationalTargetSet = true; + }); } if (gotLock) { ObjectActionSpring::updateActionWorker(deltaTimeStep); From 64c5cde2d2aee33acc43a8487401cc6dc44d9204 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:33:58 -0700 Subject: [PATCH 0152/1003] further unmangle branch split --- interface/src/avatar/AvatarActionHold.cpp | 4 ++++ libraries/physics/src/ObjectActionSpring.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 4288a354f5..3c57db084b 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -64,6 +64,10 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { _rotationalTarget = rotation; _positionalTargetSet = true; _rotationalTargetSet = true; + auto ownerEntity = _ownerEntity.lock(); + if (ownerEntity) { + ownerEntity->setActionDataDirty(true); + } }); } if (gotLock) { diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 38b4b71d26..a99cfdd747 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -219,6 +219,7 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { uint16_t serializationVersion; dataStream >> serializationVersion; if (serializationVersion != ObjectActionSpring::springVersion) { + assert(false); return; } From c884f466bc3d7977abc977f09522d4a3d91689b7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:40:58 -0700 Subject: [PATCH 0153/1003] adjust timing of action refresh --- examples/controllers/handControllerGrab.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index ee16eadeed..117ee8c453 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -66,7 +66,8 @@ var MSEC_PER_SEC = 1000.0; // these control how long an abandoned pointer line will hang around var startTime = Date.now(); var LIFETIME = 10; -var ACTION_LIFETIME = 10; // seconds +var ACTION_LIFETIME = 15; // seconds +var ACTION_LIFETIME_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; @@ -536,8 +537,8 @@ function MyController(hand, triggerAction) { this.currentObjectTime = now; Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - if (this.actionTimeout - now < MSEC_PER_SEC) { - // if less than a second left, refresh the actions lifetime + if (this.actionTimeout - now < ACTION_LIFETIME_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions lifetime Entities.updateAction(this.grabbedEntity, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, From 1d8db13f4123301a51f4919331ee99217501c89b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 10:56:47 -0700 Subject: [PATCH 0154/1003] switch to low-entity-server traffic hold --- interface/src/avatar/AvatarActionHold.cpp | 128 ++++++++++++++++------ interface/src/avatar/AvatarActionHold.h | 3 +- 2 files changed, 95 insertions(+), 36 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 3c57db084b..f12909e809 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -21,7 +21,8 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit ObjectActionSpring(id, ownerEntity), _relativePosition(glm::vec3(0.0f)), _relativeRotation(glm::quat()), - _hand("right") + _hand("right"), + _holderID(QUuid()) { _type = ACTION_TYPE_HOLD; #if WANT_DEBUG @@ -39,39 +40,41 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { bool gotLock = false; glm::quat rotation; glm::vec3 position; - glm::vec3 offset; + std::shared_ptr holdingAvatar = nullptr; gotLock = withTryReadLock([&]{ - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = myAvatar->getRightPalmPosition(); - palmRotation = myAvatar->getRightPalmRotation(); - } else { - palmPosition = myAvatar->getLeftPalmPosition(); - palmRotation = myAvatar->getLeftPalmRotation(); - } + QSharedPointer avatarManager = DependencyManager::get(); + AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); + holdingAvatar = std::static_pointer_cast(holdingAvatarData); - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; + if (holdingAvatar) { + glm::vec3 offset; + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = holdingAvatar->getRightPalmPosition(); + palmRotation = holdingAvatar->getRightPalmRotation(); + } else { + palmPosition = holdingAvatar->getLeftPalmPosition(); + palmRotation = holdingAvatar->getLeftPalmRotation(); + } + + rotation = palmRotation * _relativeRotation; + offset = rotation * _relativePosition; + position = palmPosition + offset; + } }); - if (gotLock) { - gotLock = withTryWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - _positionalTargetSet = true; - _rotationalTargetSet = true; - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - } - }); - } - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); + if (holdingAvatar) { + if (gotLock) { + gotLock = withTryWriteLock([&]{ + _positionalTarget = position; + _rotationalTarget = rotation; + }); + } + if (gotLock) { + ObjectActionSpring::updateActionWorker(deltaTimeStep); + } } } @@ -115,10 +118,11 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { holderID = myAvatar->getSessionUUID(); if (somethingChanged || - relativePosition != _relativePosition || - relativeRotation != _relativeRotation || - timeScale != _linearTimeScale || - hand != _hand) { + relativePosition != _relativePosition + || relativeRotation != _relativeRotation + || timeScale != _linearTimeScale + || hand != _hand + || holderID != _holderID) { needUpdate = true; } }); @@ -131,6 +135,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { _linearTimeScale = glm::max(MIN_TIMESCALE, timeScale); _angularTimeScale = _linearTimeScale; _hand = hand; + _holderID = holderID; _active = true; auto ownerEntity = _ownerEntity.lock(); @@ -147,6 +152,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { QVariantMap AvatarActionHold::getArguments() { QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&]{ + arguments["holderID"] = _holderID; arguments["relativePosition"] = glmToQMap(_relativePosition); arguments["relativeRotation"] = glmToQMap(_relativeRotation); arguments["timeScale"] = _linearTimeScale; @@ -156,9 +162,61 @@ QVariantMap AvatarActionHold::getArguments() { } QByteArray AvatarActionHold::serialize() const { - return ObjectActionSpring::serialize(); + QByteArray serializedActionArguments; + QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); + + withReadLock([&]{ + dataStream << ACTION_TYPE_HOLD; + dataStream << getID(); + dataStream << AvatarActionHold::holdVersion; + + dataStream << _holderID; + dataStream << _relativePosition; + dataStream << _relativeRotation; + dataStream << _linearTimeScale; + dataStream << _hand; + + dataStream << _expires; + dataStream << _tag; + }); + + return serializedActionArguments; } void AvatarActionHold::deserialize(QByteArray serializedArguments) { - assert(false); + QDataStream dataStream(serializedArguments); + + EntityActionType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != AvatarActionHold::holdVersion) { + return; + } + + withWriteLock([&]{ + dataStream >> _holderID; + dataStream >> _relativePosition; + dataStream >> _relativeRotation; + dataStream >> _linearTimeScale; + _angularTimeScale = _linearTimeScale; + dataStream >> _hand; + + dataStream >> _expires; + dataStream >> _tag; + + #if WANT_DEBUG + qDebug() << "deserialize AvatarActionHold: " << _holderID + << _relativePosition.x << _relativePosition.y << _relativePosition.z + << _hand << _expires; + #endif + + _active = true; + }); } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 840ba41c28..3cc70ced09 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressLocationEdits() { return false; } + virtual bool shouldSuppressLocationEdits() { return true; } private: static const uint16_t holdVersion; @@ -38,6 +38,7 @@ private: glm::vec3 _relativePosition; glm::quat _relativeRotation; QString _hand; + QUuid _holderID; }; #endif // hifi_AvatarActionHold_h From 605da499c8ffa059a398cedca6b390b982323386 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 15 Oct 2015 11:11:24 -0700 Subject: [PATCH 0155/1003] Updated english translation file Generated using the following command: ~/code/hifi > lupdate interface -ts interface/i18n/interface_en.ts Scanning directory 'interface'... Updating 'interface/i18n/interface_en.ts'... Found 146 source text(s) (130 new and 16 already existing) Kept 28 obsolete entries NOTE: this translation file is far from complete, many menu strings from Menu.h do not show up in this list. --- interface/i18n/interface_en.ts | 767 ++++++++++++++++++++++++++++----- 1 file changed, 648 insertions(+), 119 deletions(-) diff --git a/interface/i18n/interface_en.ts b/interface/i18n/interface_en.ts index b011f37317..53e5153501 100644 --- a/interface/i18n/interface_en.ts +++ b/interface/i18n/interface_en.ts @@ -4,61 +4,63 @@ Application - - Sparse Voxel Octree Files (*.svo) + + Yes - + Open Script - + JavaScript Files (*.js) + + AssetUploadDialogFactory + + + Successful Asset Upload + + + ChatWindow - - + Chat - - + Connecting to XMPP... - - + online now: - day - + %n day %n days - hour - + %n hour %n hours - minute - + %n minute %n minutes @@ -70,230 +72,757 @@ %n seconds + + + Console - - %1 online now: + + Dialog + + + + + > - Dialog + LoginDialog - - - - - Update Required + + Dialog - - - Download + + Authenticating... - - - Skip Version + + <style type="text/css"> + a { text-decoration: none; color: #267077;} +</style> +Invalid username or password. <a href="https://metaverse.highfidelity.com/password/new">Recover?</a> - - + + Username or Email + + + + + Password + + + + + Login + + + + + <style type="text/css"> +a { text-decoration: none; color: #267077; margin:0;padding:0;} +#create {font-weight:bold;} +p {margin:5px 0;} +</style> +<p><a id="create" href="%1">Create account</a></p> +<p><a href="%2">Recover password</a></p> + + + + + MessageDialog + + + OK + + + + + Open + + + + + Save + + + + + Save All + + + + + Retry + + + + + Ignore + + + + + Apply + + + + + Yes + + + + + Yes to All + + + + + No + + + + + No to All + + + + + Discard + + + + + Reset + + + + + Restore Defaults + + + + + Cancel + + + + + Abort + + + + Close - - - Menu - - Open .ini config file + + Show Details... - - - Text files (*.ini) + + Help - - Save .ini config file + + Hide Details PreferencesDialog - - + Cancel - - + Save all changes - - - - - Avatar - - - - - + <html><head/><body><p>Avatar display name <span style=" color:#909090;">(optional)</span></p></body></html> - - + Not showing a name - - - Head + + Avatar basics - - - Body + + <html><head/><body><p>Avatar collision sound URL <span style=" color:#909090;">(optional)</span></p></body></html> - - - Advanced Tuning + + Enter the URL of a sound to play when you bump into something - - - It's not recomended that you play with these settings unless you've looked into exactly what they do. + + <html><head/><body><p>Appearance</p></body></html> - - + + Change + + + + + Snapshots + + + + + Place my Snapshots here: + + + + + + Browse + + + + + Scripts + + + + + Load scripts from this directory: + + + + + Load Default Scripts + + + + + Privacy + + + + + Send data + + + + + Level of Detail Tuning + + + + + Minimum Desktop FPS + + + + + Minimum HMD FPS + + + + + Avatar tuning + + + + + Real world vertical field of view (angular size of monitor) + + + + Vertical field of view - - + Lean scale (applies to Faceshift users) - - + Avatar scale <span style=" color:#909090;">(default is 1.0)</span> - - - Pupil dillation + + Pupil dilation - - - Audio Jitter Buffer Samples (0 for automatic) + + Camera binary eyelid threshold - - - Faceshift eye detection + + Face tracker eye deflection - - + + Faceshift hostname + + + + + localhost + + + + + Avatar Animation JSON + + + + + default + + + + + Audio + + + + + Enable Dynamic Jitter Buffers + + + + + Static Jitter Buffer Frames + + + + + Max Frames Over Desired + + + + + Use Stdev for Dynamic Jitter Calc + + + + + Window A Starve Threshold + + + + + Window A (raise desired on N starves) Seconds + + + + + Window B (desired ceiling) Seconds + + + + + Repetition with Fade + + + + + Output Buffer Size (Frames) + + + + + Output Starve Detection (Automatic Buffer Size Increase) + + + + + Output Starve Detection Threshold + + + + + Output Starve Detection Period (ms) + + + + + Oculus Rift + + + + + User Interface Angular Size + + + + + Sixense Controllers + + + + + Reticle Movement Speed + + + + Octree - - + Max packets sent each second + + + Snapshots Location + + + + + Scripts Location + + - QObject + RealSense - - Loading ... + + Open RSSDK clip - - Cancel + + RSSDK Recordings (*.rssdk) RunningScriptsWidget - - - Form + + Running Scripts - - - <html><head/><body><p><span style=" font-size:18pt;">Running Scripts</span></p></body></html> + + Currently Running - - - <html><head/><body><p><span style=" font-weight:600;">Currently running</span></p></body></html> - - - - - + Reload all Reload All - - + Stop all Stop All - - - <html><head/><body><p><span style=" font-weight:600;">Recently loaded</span></p></body></html> + + There are no scripts running. - - - (click a script or use the 1-9 keys to load and run it) + + Load Scripts - - - There are no scripts currently running. + + from URL + + + + + from Disk + + + + + filter + + + + + ScriptEditorWidget + + + Edit Script + + + + + Debug Log: + + + + + Run on the fly (Careful: Any valid change made to the code will run immediately) + + + + + Clear + + + + + + + Interface + + + + + Cannot write script %1: +%2. + + + + + Cannot read script %1: +%2. + + + + + Save script + + + + + JavaScript Files (*.js) + + + + + The script has been modified. +Do you want to save your changes? + + + + + This file has been modified outside of the Interface editor. + + + + + Do you want to reload it and lose the changes you've made in the Interface editor? + + + + + Do you want to reload it? + + + + + ScriptEditorWindow + + + Script Editor + + + + + New Script (Ctrl+N) + + + + + New + + + + + Load Script (Ctrl+O) + + + + + Load + + + + + Save Script (Ctrl+S) + + + + + Save + + + + + Toggle Run Script (F5) + + + + + Run/Stop + + + + + Automatically reload externally changed files + + + + + + Interface + + + + + JavaScript Files (*.js) + + + + + There are some unsaved scripts, are you sure you want to close the editor? Changes will be lost! + + + + + ScriptsModel + + + / + + + + + SnapshotShareDialog + + + Share with Alphas + + + + + Notes about this image + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Helvetica'; font-size:14px; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + Share + + + + + TestMenu + + + About Interface + + + + + Login + + + + + Quit + + + + + Animations... + + + + + Attachments... + + + + + Explode on quit + + + + + Freeze on quit + + + + + Everyone + + + + + Friends + + + + + No one + + + + + UserLocationsDialog + + + Form + + + + + My Locations + + + + + Refresh + + + + + Rename + + + + + Delete From 3b59bffb79ae88caa6e703e52cc6120132729379 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 15 Oct 2015 11:38:18 -0700 Subject: [PATCH 0156/1003] Deleting only strokes from the whiteboard where the erase button was clicked on --- .../whiteboard/whiteboardEntityScript.js | 23 ++++++++++++++----- .../painting/whiteboard/whiteboardSpawner.js | 2 +- libraries/octree/src/Octree.cpp | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 9ed5d21065..cff028c566 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -93,7 +93,7 @@ this.oldPosition = null; } } - } else if(this.intersection.properties.type !== "Unknown") { + } else if (this.intersection.properties.type !== "Unknown") { //Sometimes ray will pick against an invisible object with type unkown... so if type is unknown, ignore this.stopPainting(); } @@ -169,7 +169,10 @@ y: 50, z: 50 }, - lifetime: 200 + lifetime: 200, + userData: JSON.stringify({ + whiteboard: this.entityID + }) }); this.strokePoints = []; this.strokeNormals = []; @@ -194,10 +197,17 @@ }, eraseBoard: function() { - var entities = Entities.findEntities(this.position, 5); + var distance = Math.max(this.dimensions.x, this.dimensions.y); + var entities = Entities.findEntities(this.position, distance); entities.forEach(function(entity) { - var name = Entities.getEntityProperties(entity, "name").name; - if (name === "paintStroke") { + var props = Entities.getEntityProperties(entity, ["name, userData"]); + var name = props.name; + if(!props.userData) { + return; + } + var whiteboardID = JSON.parse(props.userData).whiteboard; + if (name === "paintStroke" && JSON.stringify(whiteboardID) === JSON.stringify(_this.entityID)) { + // This entity is a paintstroke and part of this whiteboard so delete it Entities.deleteEntity(entity); } }); @@ -205,9 +215,10 @@ preload: function(entityID) { this.entityID = entityID; - var props = Entities.getEntityProperties(this.entityID, ["position", "rotation", "userData"]); + var props = Entities.getEntityProperties(this.entityID, ["position", "rotation", "userData", "dimensions"]); this.position = props.position; this.rotation = props.rotation; + this.dimensions = props.dimensions; this.normal = Vec3.multiply(Quat.getFront(this.rotation), -1); this.painting = false; this.strokes = []; diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 4219ee30bc..51e4dee87d 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -189,4 +189,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -Script.scriptEnding.connect(cleanup); \ No newline at end of file +// Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 6f6f5af303..3b6467401c 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -724,7 +724,7 @@ bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, intersectedObject, false, precisionPicking}; + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking}; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; From b7c40c2df198d28972657d50af65fa11b991be4b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Oct 2015 11:41:04 -0700 Subject: [PATCH 0157/1003] fix Queue OUT stat --- libraries/networking/src/ReceivedPacketProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ReceivedPacketProcessor.cpp b/libraries/networking/src/ReceivedPacketProcessor.cpp index cc516bdbbd..1afb84624f 100644 --- a/libraries/networking/src/ReceivedPacketProcessor.cpp +++ b/libraries/networking/src/ReceivedPacketProcessor.cpp @@ -45,7 +45,7 @@ bool ReceivedPacketProcessor::process() { float incomingPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow; _incomingPPS.updateAverage(incomingPacketsPerSecondInWindow); - float processedPacketsPerSecondInWindow = (float)_lastWindowIncomingPackets / secondsSinceLastWindow; + float processedPacketsPerSecondInWindow = (float)_lastWindowProcessedPackets / secondsSinceLastWindow; _processedPPS.updateAverage(processedPacketsPerSecondInWindow); _lastWindowAt = now; From a70ba4cd5a66c91c4fc6504f351b6a6f367dfa8d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 11:42:53 -0700 Subject: [PATCH 0158/1003] add some accessors --- interface/src/avatar/Avatar.h | 2 ++ interface/src/avatar/MyAvatar.h | 2 ++ libraries/animation/src/Rig.cpp | 7 +++++++ libraries/render-utils/src/Model.h | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 9a46a145c2..1ad4411619 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -174,6 +174,8 @@ public: void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } AvatarMotionState* getMotionState() { return _motionState; } + virtual RigPointer getRig() const { return _skeletonModel.getRig(); } + public slots: glm::vec3 getLeftPalmPosition(); glm::vec3 getLeftPalmVelocity(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7347419fee..eb907de4ef 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -183,6 +183,8 @@ public: glm::quat getCustomListenOrientation() { return _customListenOrientation; } void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; } + virtual RigPointer getRig() const { return _rig; } + public slots: void increaseSize(); void decreaseSize(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 25f28a3f64..241fdf10bc 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -50,6 +50,13 @@ void Rig::HeadParameters::dump() const { qCDebug(animation, " isTalking = %s", isTalking ? "true" : "false"); } +// QString Rig::HeadParameters::dumpJoint(int jointIndex) const { +// QString out = ""; +// out += ...; +// JointState& state = _jointStates[i]; +// int parentIndex = state.getParentIndex(); +// } + void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e3a9ce9ac3..f5d5f40363 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -198,6 +198,10 @@ public: return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index); } + virtual RigPointer getRig() const { return _rig; } + + const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } + protected: void setPupilDilation(float dilation) { _pupilDilation = dilation; } From dc6875f5b582d648b35bd94f14dbf165d608e424 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 15 Oct 2015 11:50:23 -0700 Subject: [PATCH 0159/1003] Strokes moving forward to avoid zfighting and incorrect overlaps --- .../painting/whiteboard/whiteboardEntityScript.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index cff028c566..b03ab8f86c 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -20,7 +20,7 @@ var _this; var RIGHT_HAND = 1; var LEFT_HAND = 0; - var MIN_POINT_DISTANCE = 0.02; + var MIN_POINT_DISTANCE = 0.01 ; var MAX_POINT_DISTANCE = 0.5; var MAX_POINTS_PER_LINE = 40; var MAX_DISTANCE = 5; @@ -119,8 +119,9 @@ var localPoint = Vec3.subtract(position, this.strokeBasePosition); - //Move stroke a bit forward along normal so it doesnt zfight with mesh its drawing on - localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, 0.001 + Math.random() * 0.001)); //rand avoid z fighting + //Move stroke a bit forward along normal each point so it doesnt zfight with mesh its drawing on, or previous part of stroke(s) + localPoint = Vec3.sum(localPoint, Vec3.multiply(this.normal, this.forwardOffset)); + this.forwardOffset += 0.00001; var distance = Vec3.distance(localPoint, this.strokePoints[this.strokePoints.length - 1]); if (this.strokePoints.length > 0 && distance < MIN_POINT_DISTANCE) { //need a minimum distance to avoid binormal NANs @@ -157,7 +158,6 @@ newStroke: function(position) { - this.strokeBasePosition = position; this.currentStroke = Entities.addEntity({ position: position, @@ -231,12 +231,11 @@ solid: true, rotation: this.rotation }); + this.forwardOffset = 0.0005; }, unload: function() { - this.strokes.forEach(function(stroke) { - Entities.deleteEntity(stroke); - }); + Overlays.deleteOverlay(this.laserPointer); } From 93113af2d2d8b193148da4cf5254d9df4c36e2d9 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Oct 2015 11:55:20 -0700 Subject: [PATCH 0160/1003] improve server error message for failed edit --- libraries/entities/src/EntityTree.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c4c02d364f..764f7fd421 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -647,8 +647,10 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi } } else { static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("^Add or Edit failed.*"); - qCDebug(entities) << "Add or Edit failed." << packet.getType() << existingEntity.get(); + LogHandler::getInstance().addRepeatedMessageRegex("^Edit failed.*"); + qCDebug(entities) << "Edit failed. [" << packet.getType() <<"] " << + "entity id:" << entityItemID << + "existingEntity pointer:" << existingEntity.get(); } } From 923e1ed208936788deeb33e98bf4bf699e715a56 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Oct 2015 12:21:36 -0700 Subject: [PATCH 0161/1003] add more processing stats --- interface/src/ui/OctreeStatsDialog.cpp | 31 ++++++++++++++++++++++---- interface/src/ui/OctreeStatsDialog.h | 3 ++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/OctreeStatsDialog.cpp b/interface/src/ui/OctreeStatsDialog.cpp index d3ff017633..42f31f8b1e 100644 --- a/interface/src/ui/OctreeStatsDialog.cpp +++ b/interface/src/ui/OctreeStatsDialog.cpp @@ -54,11 +54,13 @@ OctreeStatsDialog::OctreeStatsDialog(QWidget* parent, NodeToOctreeSceneStats* mo _localElementsMemory = AddStatItem("Elements Memory"); _sendingMode = AddStatItem("Sending Mode"); - _processedPackets = AddStatItem("Entity Packets"); + _processedPackets = AddStatItem("Incoming Entity Packets"); _processedPacketsElements = AddStatItem("Processed Packets Elements"); _processedPacketsEntities = AddStatItem("Processed Packets Entities"); _processedPacketsTiming = AddStatItem("Processed Packets Timing"); + _outboundEditPackets = AddStatItem("Outbound Entity Packets"); + _entityUpdateTime = AddStatItem("Entity Update Time"); _entityUpdates = AddStatItem("Entity Updates"); @@ -266,6 +268,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { label = _labels[_processedPackets]; const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor(); + auto incomingPacketsDepth = entitiesPacketProcessor.packetsToProcessCount(); auto incomingPPS = entitiesPacketProcessor.getIncomingPPS(); auto processedPPS = entitiesPacketProcessor.getProcessedPPS(); auto treeProcessedPPS = entities->getAveragePacketsPerSecond(); @@ -276,6 +279,7 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { statsValue.str(""); statsValue << + "Queue Size: " << incomingPacketsDepth << " Packets / " << "Network IN: " << qPrintable(incomingPPSString) << " PPS / " << "Queue OUT: " << qPrintable(processedPPSString) << " PPS / " << "Tree IN: " << qPrintable(treeProcessedPPSString) << " PPS"; @@ -301,12 +305,31 @@ void OctreeStatsDialog::paintEvent(QPaintEvent* event) { label = _labels[_processedPacketsTiming]; statsValue.str(""); statsValue << - "Lock Wait:" << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " << - "Uncompress:" << qPrintable(averageUncompressPerPacketString) << " (usecs) / " << - "Process:" << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)"; + "Lock Wait: " << qPrintable(averageWaitLockPerPacketString) << " (usecs) / " << + "Uncompress: " << qPrintable(averageUncompressPerPacketString) << " (usecs) / " << + "Process: " << qPrintable(averageReadBitstreamPerPacketString) << " (usecs)"; label->setText(statsValue.str().c_str()); + auto entitiesEditPacketSender = qApp->getEntityEditPacketSender(); + + auto outboundPacketsDepth = entitiesEditPacketSender->packetsToSendCount(); + auto outboundQueuedPPS = entitiesEditPacketSender->getLifetimePPSQueued(); + auto outboundSentPPS = entitiesEditPacketSender->getLifetimePPS(); + + QString outboundQueuedPPSString = locale.toString(outboundQueuedPPS, 'f', FLOATING_POINT_PRECISION); + QString outboundSentPPSString = locale.toString(outboundSentPPS, 'f', FLOATING_POINT_PRECISION); + + label = _labels[_outboundEditPackets]; + statsValue.str(""); + statsValue << + "Queue Size: " << outboundPacketsDepth << " packets / " << + "Queued IN: " << qPrintable(outboundQueuedPPSString) << " PPS / " << + "Sent OUT: " << qPrintable(outboundSentPPSString) << " PPS"; + + label->setText(statsValue.str().c_str()); + + // Entity Edits update time label = _labels[_entityUpdateTime]; auto averageEditDelta = entitiesTree->getAverageEditDeltas(); diff --git a/interface/src/ui/OctreeStatsDialog.h b/interface/src/ui/OctreeStatsDialog.h index 063c04b295..97f87849ec 100644 --- a/interface/src/ui/OctreeStatsDialog.h +++ b/interface/src/ui/OctreeStatsDialog.h @@ -70,10 +70,11 @@ private: int _processedPacketsElements; int _processedPacketsEntities; int _processedPacketsTiming; + int _outboundEditPackets; const int SAMPLES_PER_SECOND = 10; SimpleMovingAverage _averageUpdatesPerSecond; - quint64 _lastWindowAt = 0; + quint64 _lastWindowAt = usecTimestampNow(); quint64 _lastKnownTrackedEdits = 0; quint64 _lastRefresh = 0; From 4f8e2c9f6b30b91f2f9b2b998043ef12e0155c18 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 15 Oct 2015 13:18:04 -0700 Subject: [PATCH 0162/1003] 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 0163/1003] 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 e8b6b32572afc075b9c21a44b15f4f19d3f6b6c4 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 15 Oct 2015 14:12:15 -0700 Subject: [PATCH 0164/1003] Remembering whiteboards last color between sessions --- .../whiteboard/colorIndicatorEntityScript.js | 2 +- .../whiteboard/colorSelectorEntityScript.js | 7 +++---- .../painting/whiteboard/whiteboardEntityScript.js | 11 +++++++---- examples/painting/whiteboard/whiteboardSpawner.js | 13 ++++++++----- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/examples/painting/whiteboard/colorIndicatorEntityScript.js b/examples/painting/whiteboard/colorIndicatorEntityScript.js index 326e9ab9c3..27b8ca01a1 100644 --- a/examples/painting/whiteboard/colorIndicatorEntityScript.js +++ b/examples/painting/whiteboard/colorIndicatorEntityScript.js @@ -22,7 +22,7 @@ changeColor: function() { var userData = JSON.parse(Entities.getEntityProperties(this.whiteboard, "userData").userData); - var newColor = userData.currentColor; + var newColor = userData.color.currentColor; Entities.editEntity(this.entityID, { color: newColor }); diff --git a/examples/painting/whiteboard/colorSelectorEntityScript.js b/examples/painting/whiteboard/colorSelectorEntityScript.js index 8ad003035c..f1105604c7 100644 --- a/examples/painting/whiteboard/colorSelectorEntityScript.js +++ b/examples/painting/whiteboard/colorSelectorEntityScript.js @@ -12,7 +12,7 @@ /*global ColorSelector */ (function() { - + Script.include("../../libraries/utils.js"); var _this; ColorSelector = function() { _this = this; @@ -29,9 +29,7 @@ }, selectColor: function() { - Entities.editEntity(this.whiteboard, { - userData: JSON.stringify({currentColor: this.color}) - }); + setEntityCustomData(this.colorKey, this.whiteboard, {currentColor: this.color}); Entities.callEntityMethod(this.whiteboard, "changeColor"); }, @@ -40,6 +38,7 @@ var props = Entities.getEntityProperties(this.entityID, ["position, color, userData"]); this.position = props.position; this.color = props.color; + this.colorKey = "color"; this.whiteboard = JSON.parse(props.userData).whiteboard; }, diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index b03ab8f86c..f38073f389 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -188,11 +188,14 @@ }, changeColor: function() { - this.strokeColor = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData).currentColor; + var userData = JSON.parse(Entities.getEntityProperties(this.entityID, ["userData"]).userData); + this.strokeColor = userData.color.currentColor; + this.colorIndicator = userData.colorIndicator; Overlays.editOverlay(this.laserPointer, { color: this.strokeColor }); + Entities.callEntityMethod(this.colorIndicator, "changeColor"); }, @@ -223,20 +226,20 @@ this.painting = false; this.strokes = []; this.whitelist = [this.entityID]; - var userData = JSON.parse(props.userData); - this.strokeColor = userData.currentColor; - this.colorIndicator = userData.colorIndicator; this.laserPointer = Overlays.addOverlay("circle3d", { color: this.strokeColor, solid: true, rotation: this.rotation }); this.forwardOffset = 0.0005; + + this.changeColor(); }, unload: function() { Overlays.deleteOverlay(this.laserPointer); + // this.eraseBoard(); } }; diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 51e4dee87d..13588d24a8 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -49,10 +49,7 @@ var whiteboard = Entities.addEntity({ red: 255, green: 255, blue: 255 - }, - userData: JSON.stringify({ - currentColor: colors[0] - }) + } }); @@ -70,6 +67,7 @@ var colorIndicatorPosition = Vec3.sum(center, { }); var colorIndicatorBox = Entities.addEntity({ type: "Box", + name: "Color Indicator", color: colors[0], rotation: rotation, position: colorIndicatorPosition, @@ -82,7 +80,9 @@ var colorIndicatorBox = Entities.addEntity({ Entities.editEntity(whiteboard, { userData: JSON.stringify({ - currentColor: colors[0], + color: { + currentColor: colors[0], + }, colorIndicator: colorIndicatorBox }) }); @@ -102,6 +102,7 @@ var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { var colorBox = Entities.addEntity({ type: "Box", + name: "Color Selector", position: colorBoxPosition, dimensions: colorSquareDimensions, rotation: rotation, @@ -127,6 +128,7 @@ colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDime colorBoxPosition.y += 0.3; var blackBox = Entities.addEntity({ type: 'Box', + name: "Black Color", position: colorBoxPosition, dimensions: blackBoxDimensions, rotation: rotation, @@ -155,6 +157,7 @@ scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraseAllText = Entities.addEntity({ type: "Text", position: eraseBoxPosition, + name: "Eraser", script: scriptURL, rotation: rotation, dimensions: eraseBoxDimensions, From 8b57b3c0ff1d203d86d75b7f4f3382e44c704cc1 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 15 Oct 2015 14:13:07 -0700 Subject: [PATCH 0165/1003] fix memory leak --- interface/src/scripting/WebWindowClass.cpp | 3 +++ interface/src/ui/ToolWindow.cpp | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 7e998ed8ff..027a8eb123 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -52,6 +52,9 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid dockWidget->setWidget(_webView); + auto titleWidget = new QWidget(); + dockWidget->setTitleBarWidget(titleWidget); + toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal); _windowWidget = dockWidget; diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index f5dcf279c9..fb7e618428 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -33,16 +33,22 @@ ToolWindow::ToolWindow(QWidget* parent) : bool ToolWindow::event(QEvent* event) { QEvent::Type type = event->type(); if (type == QEvent::Show) { + if (!_hasShown) { _hasShown = true; QMainWindow* mainWindow = qApp->getWindow(); QRect mainGeometry = mainWindow->geometry(); - _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y(), - DEFAULT_WIDTH, mainGeometry.height()); + int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); + int topMargin = titleBarHeight; + + _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, + DEFAULT_WIDTH, mainGeometry.height() - topMargin); } + setGeometry(_lastGeometry); + return true; } else if (type == QEvent::Hide) { _lastGeometry = geometry(); @@ -125,7 +131,6 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, QDockWidget* lastDockWidget = dockWidget; - lastDockWidget->setTitleBarWidget(new QWidget()); foreach(QDockWidget* nextDockWidget, dockWidgets) { tabifyDockWidget(lastDockWidget, nextDockWidget); From 4903db45b14800cad0200f85b97bb1c7a13b0520 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 14:21:06 -0700 Subject: [PATCH 0166/1003] give other avatars an anim-skeleton --- interface/src/avatar/AvatarActionHold.cpp | 3 +++ interface/src/avatar/SkeletonModel.cpp | 12 ++++++------ libraries/animation/src/Rig.cpp | 8 +++++++- libraries/animation/src/Rig.h | 1 + libraries/render-utils/src/Model.cpp | 2 ++ 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index f12909e809..018108ac52 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -70,6 +70,9 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { gotLock = withTryWriteLock([&]{ _positionalTarget = position; _rotationalTarget = rotation; + _positionalTargetSet = true; + _rotationalTargetSet = true; + _active = true; }); } if (gotLock) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 312b7cbf44..28c7941c52 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -229,6 +229,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Model::simulate(deltaTime, fullUpdate); + // let rig compute the model offset + glm::vec3 modelOffset; + if (_rig->getModelOffset(modelOffset)) { + setOffset(modelOffset); + } + if (!isActive() || !_owningAvatar->isMyAvatar()) { return; // only simulate for own avatar } @@ -246,12 +252,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Hand* hand = _owningAvatar->getHand(); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - // let rig compute the model offset - glm::vec3 modelOffset; - if (_rig->getModelOffset(modelOffset)) { - setOffset(modelOffset); - } - // Don't Relax toward hand positions when in animGraph mode. if (!_rig->getEnableAnimGraph()) { const float HAND_RESTORATION_RATE = 0.25f; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 241fdf10bc..fd764964a4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1245,12 +1245,18 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { } } +void Rig::makeAnimSkeleton(const FBXGeometry& fbxGeometry) { + if (!_animSkeleton) { + _animSkeleton = std::make_shared(fbxGeometry); + } +} + void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { if (!_enableAnimGraph) { return; } - _animSkeleton = std::make_shared(fbxGeometry); + makeAnimSkeleton(fbxGeometry); // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 71c27e7213..c9c42cbbb6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -193,6 +193,7 @@ public: virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) = 0; + void makeAnimSkeleton(const FBXGeometry& fbxGeometry); void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry); AnimNode::ConstPointer getAnimNode() const { return _animNode; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 38f5ffdabe..6aae7ad1cb 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1147,6 +1147,8 @@ void Model::segregateMeshGroups() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const std::vector>& networkMeshes = _geometry->getMeshes(); + _rig->makeAnimSkeleton(geometry); + // all of our mesh vectors must match in size if ((int)networkMeshes.size() != geometry.meshes.size() || geometry.meshes.size() != _meshStates.size()) { From 416df1c44c7f1c181ab7fd81e66f5888a3bfb4a2 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 14:21:08 -0700 Subject: [PATCH 0167/1003] 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 0168/1003] 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 487131dde1d55634065de7a0dfbf5838a1e28efe Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 14:52:38 -0700 Subject: [PATCH 0169/1003] cleanups --- interface/src/avatar/Avatar.h | 2 -- interface/src/avatar/AvatarActionHold.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 1ad4411619..9a46a145c2 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -174,8 +174,6 @@ public: void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; } AvatarMotionState* getMotionState() { return _motionState; } - virtual RigPointer getRig() const { return _skeletonModel.getRig(); } - public slots: glm::vec3 getLeftPalmPosition(); glm::vec3 getLeftPalmVelocity(); diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 018108ac52..561a2bbcc5 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -121,11 +121,11 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { holderID = myAvatar->getSessionUUID(); if (somethingChanged || - relativePosition != _relativePosition - || relativeRotation != _relativeRotation - || timeScale != _linearTimeScale - || hand != _hand - || holderID != _holderID) { + relativePosition != _relativePosition || + relativeRotation != _relativeRotation || + timeScale != _linearTimeScale || + hand != _hand || + holderID != _holderID) { needUpdate = true; } }); From 462918ffcf1fc64f78c2fa7a4f32670500da33b1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 14:55:17 -0700 Subject: [PATCH 0170/1003] cleanups --- interface/src/avatar/MyAvatar.h | 2 -- libraries/animation/src/Rig.cpp | 7 ------- 2 files changed, 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index eb907de4ef..7347419fee 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -183,8 +183,6 @@ public: glm::quat getCustomListenOrientation() { return _customListenOrientation; } void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; } - virtual RigPointer getRig() const { return _rig; } - public slots: void increaseSize(); void decreaseSize(); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index fd764964a4..6fd6f5cbf9 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -50,13 +50,6 @@ void Rig::HeadParameters::dump() const { qCDebug(animation, " isTalking = %s", isTalking ? "true" : "false"); } -// QString Rig::HeadParameters::dumpJoint(int jointIndex) const { -// QString out = ""; -// out += ...; -// JointState& state = _jointStates[i]; -// int parentIndex = state.getParentIndex(); -// } - void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { From 112007e5a948deb64be34649abcaf4c4a44279a9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 15 Oct 2015 14:58:05 -0700 Subject: [PATCH 0171/1003] cleanup --- interface/src/scripting/WebWindowClass.cpp | 2 +- interface/src/ui/ToolWindow.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 027a8eb123..69523f5f9d 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -52,7 +52,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid dockWidget->setWidget(_webView); - auto titleWidget = new QWidget(); + auto titleWidget = new QWidget(dockWidget); dockWidget->setTitleBarWidget(titleWidget); toolWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget, Qt::Horizontal); diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index fb7e618428..f195535433 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -127,11 +127,9 @@ void ToolWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, QList dockWidgets = findChildren(); QMainWindow::addDockWidget(area, dockWidget, orientation); - QDockWidget* lastDockWidget = dockWidget; - foreach(QDockWidget* nextDockWidget, dockWidgets) { tabifyDockWidget(lastDockWidget, nextDockWidget); lastDockWidget = nextDockWidget; From 1b03b6867c075a1c4b4430a0a682a8a4d9fb62aa Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 15:01:34 -0700 Subject: [PATCH 0172/1003] 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 0173/1003] 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 82a211174bf4fc361b75c7dc10102d95ccca4449 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Oct 2015 15:23:12 -0700 Subject: [PATCH 0174/1003] added packet recieved,lost,recovered stats to detailed server stats page --- assignment-client/src/octree/OctreeServer.cpp | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index ee0403d57b..c3d3d9094d 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -692,6 +692,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url totalElementsProcessed = senderStats.getTotalElementsProcessed(); totalPacketsProcessed = senderStats.getTotalPacketsProcessed(); + + auto received = senderStats._incomingEditSequenceNumberStats.getReceived(); + auto expected = senderStats._incomingEditSequenceNumberStats.getExpectedReceived(); + auto unreasonable = senderStats._incomingEditSequenceNumberStats.getUnreasonable(); + auto outOfOrder = senderStats._incomingEditSequenceNumberStats.getOutOfOrder(); + auto early = senderStats._incomingEditSequenceNumberStats.getEarly(); + auto late = senderStats._incomingEditSequenceNumberStats.getLate(); + auto lost = senderStats._incomingEditSequenceNumberStats.getLost(); + auto recovered = senderStats._incomingEditSequenceNumberStats.getRecovered(); + averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed; statsString += QString(" Total Inbound Packets: %1 packets\r\n") @@ -702,7 +712,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url (double)averageElementsPerPacket); statsString += QString(" Average Transit Time/Packet: %1 usecs\r\n") .arg(locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ')); - statsString += QString(" Average Process Time/Packet: %1 usecs\r\n") + statsString += QString(" Average Process Time/Packet: %1 usecs\r\n") .arg(locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Average Wait Lock Time/Packet: %1 usecs\r\n") .arg(locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ')); @@ -711,6 +721,24 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n") .arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString("\r\n Inbound Edit Packets --------------------------------\r\n"); + statsString += QString(" Received: %1\r\n") + .arg(locale.toString(received).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Expected: %1\r\n") + .arg(locale.toString(expected).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Unreasonable: %1\r\n") + .arg(locale.toString(unreasonable).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Out of Order: %1\r\n") + .arg(locale.toString(outOfOrder).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Early: %1\r\n") + .arg(locale.toString(early).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Late: %1\r\n") + .arg(locale.toString(late).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Lost: %1\r\n") + .arg(locale.toString(lost).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Recovered: %1\r\n") + .arg(locale.toString(recovered).rightJustified(COLUMN_WIDTH, ' ')); + } statsString += "\r\n\r\n"; From e1570e319737335e991f034e36752734f3b469dd Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Oct 2015 15:31:52 -0700 Subject: [PATCH 0175/1003] include inbound queue PPS stats in server stats page --- assignment-client/src/octree/OctreeServer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index c3d3d9094d..4bd1211547 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -625,6 +625,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += QString().sprintf("%s Edit Statistics... [RESET]\r\n", getMyServerName()); quint64 currentPacketsInQueue = _octreeInboundPacketProcessor->packetsToProcessCount(); + float incomingPPS = _octreeInboundPacketProcessor->getIncomingPPS(); + float processedPPS = _octreeInboundPacketProcessor->getProcessedPPS(); quint64 averageTransitTimePerPacket = _octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); quint64 averageProcessTimePerPacket = _octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); quint64 averageLockWaitTimePerPacket = _octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); @@ -639,11 +641,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url quint64 averageCreateTime = _tree->getAverageCreateTime(); quint64 averageLoggingTime = _tree->getAverageLoggingTime(); + int FLOAT_PRECISION = 3; float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : (float)totalElementsProcessed / totalPacketsProcessed; - statsString += QString(" Current Inbound Packets Queue: %1 packets\r\n") + statsString += QString(" Current Inbound Packets Queue: %1 packets \r\n") .arg(locale.toString((uint)currentPacketsInQueue).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Packets Queue Network IN: %1 PPS \r\n") + .arg(locale.toString(incomingPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Packets Queue Processing OUT: %1 PPS \r\n") + .arg(locale.toString(incomingPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Total Inbound Packets: %1 packets\r\n") .arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ')); From 3718149157cb82eecf92620b07c0c182e118fd41 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 15 Oct 2015 15:49:12 -0700 Subject: [PATCH 0176/1003] include inbound queue PPS stats in server stats page --- assignment-client/src/octree/OctreeServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 4bd1211547..818d54ee97 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -650,7 +650,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += QString(" Packets Queue Network IN: %1 PPS \r\n") .arg(locale.toString(incomingPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Packets Queue Processing OUT: %1 PPS \r\n") - .arg(locale.toString(incomingPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' ')); + .arg(locale.toString(processedPPS, 'f', FLOAT_PRECISION).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Total Inbound Packets: %1 packets\r\n") .arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ')); From 97fe0170cbc5a20aa6d89dd83176082cbbe3e67d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 15 Oct 2015 17:09:41 -0700 Subject: [PATCH 0177/1003] glowing box for black color --- examples/painting/whiteboard/blackInk.fs | 23 +++++++++++++++++++ .../painting/whiteboard/whiteboardSpawner.js | 18 ++++++++++----- 2 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 examples/painting/whiteboard/blackInk.fs diff --git a/examples/painting/whiteboard/blackInk.fs b/examples/painting/whiteboard/blackInk.fs new file mode 100644 index 0000000000..1e77db285c --- /dev/null +++ b/examples/painting/whiteboard/blackInk.fs @@ -0,0 +1,23 @@ +vec2 iResolution = iWorldScale.xy; +vec2 iMouse = vec2(0); + +const float PI = 3.14159265; + +float time = iGlobalTime; +vec2 center = vec2(0.5, 0.5); +void mainImage( out vec4 fragColor, in vec2 fragCoord ) { + vec2 position = (fragCoord.xy/iResolution.xy) + 0.5; + float dist = pow(distance(position.xy, center), 3.); + dist = dist / 1.0 + sin(time * 10)/100.0; + vec3 color = vec3(dist, 0.0, dist); + fragColor = vec4(color, 1.0); +} + +vec4 getProceduralColor() { + vec4 result; + vec2 position = _position.xy; + + mainImage(result, position * iWorldScale.xy); + + return result; +} \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 13588d24a8..cbc26da670 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -81,7 +81,7 @@ var colorIndicatorBox = Entities.addEntity({ Entities.editEntity(whiteboard, { userData: JSON.stringify({ color: { - currentColor: colors[0], + currentColor: colors[0] }, colorIndicator: colorIndicatorBox }) @@ -120,12 +120,14 @@ for (var i = 0; i < colors.length; i++) { // BLACK BOX var blackBoxDimensions = { - x: .2, - y: .2, - z: 0.05 + x: 0.3, + y: 0.3, + z: 0.01 }; + colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x / 2 - 0.01)); colorBoxPosition.y += 0.3; +var fragShaderURL = Script.resolvePath('blackInk.fs?v1' + Math.random()); var blackBox = Entities.addEntity({ type: 'Box', name: "Black Color", @@ -139,7 +141,11 @@ var blackBox = Entities.addEntity({ }, script: scriptURL, userData: JSON.stringify({ - whiteboard: whiteboard + whiteboard: whiteboard, + version: 2, + ProceduralEntity: { + shaderUrl: fragShaderURL + } }) }); @@ -192,4 +198,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 0964180f2cbcbb801dc28e2bf99ba7ae51fdee02 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 17:10:47 -0700 Subject: [PATCH 0178/1003] code review --- interface/src/avatar/AvatarActionHold.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 561a2bbcc5..815293bf0d 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -74,9 +74,9 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { _rotationalTargetSet = true; _active = true; }); - } - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); + if (gotLock) { + ObjectActionSpring::updateActionWorker(deltaTimeStep); + } } } } From 43d7fe491ec4720df6976be638a41b9620534860 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 15 Oct 2015 17:53:53 -0700 Subject: [PATCH 0179/1003] 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 a84fdecec9f0735bf18c9585f86055797cffe556 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 15 Oct 2015 18:43:32 -0700 Subject: [PATCH 0180/1003] Fix export bug --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 2 +- libraries/entities/src/EntityItemProperties.h | 6 +++++- libraries/entities/src/EntityScriptingInterface.cpp | 2 +- libraries/entities/src/EntityTypes.cpp | 4 +++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index f4f8c4c2f7..8a973c88a9 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -29,7 +29,7 @@ EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityI RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : ModelEntityItem(entityItemID, properties), - _dimensionsInitialized { properties.dimensionsChanged() } + _dimensionsInitialized(properties.getDimensionsInitialized()) { } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4ed7454c17..15c6669876 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -254,7 +254,10 @@ public: void setActionDataDirty() { _actionDataChanged = true; } QList listChangedProperties(); - + + bool getDimensionsInitialized() const { return _dimensionsInitialized; } + void setDimensionsInitialized(bool dimensionsInitialized) { _dimensionsInitialized = dimensionsInitialized; } + private: QUuid _id; bool _idSet; @@ -267,6 +270,7 @@ private: bool _glowLevelChanged; bool _localRenderAlphaChanged; bool _defaultSettings; + bool _dimensionsInitialized = false; // Only true if creating an entity localy with no dimensions properties // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3a530c0f3a..07c932b34e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -65,8 +65,8 @@ void EntityScriptingInterface::setEntityTree(EntityTreePointer elementTree) { } QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { - EntityItemProperties propertiesWithSimID = properties; + propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); EntityItemID id = EntityItemID(QUuid::createUuid()); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 1ef421efca..52c2242629 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -88,7 +88,9 @@ EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const factory = _factories[entityType]; } if (factory) { - newEntityItem = factory(entityID, properties); + auto mutableProperties = properties; + mutableProperties.markAllChanged(); + newEntityItem = factory(entityID, mutableProperties); } return newEntityItem; } From 359a318568f4fb8acc9777a901bc0cdf7c2b3dc3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Oct 2015 20:23:06 -0700 Subject: [PATCH 0181/1003] fold kinematic hold into normal hold (disabled by default, an argument enables) --- interface/src/InterfaceActionFactory.cpp | 3 - interface/src/avatar/AvatarActionHold.cpp | 65 +++++- interface/src/avatar/AvatarActionHold.h | 7 + .../src/avatar/AvatarActionKinematicHold.cpp | 188 ------------------ .../src/avatar/AvatarActionKinematicHold.h | 47 ----- .../entities/src/EntityActionInterface.cpp | 22 +- .../entities/src/EntityActionInterface.h | 5 +- 7 files changed, 90 insertions(+), 247 deletions(-) delete mode 100644 interface/src/avatar/AvatarActionKinematicHold.cpp delete mode 100644 interface/src/avatar/AvatarActionKinematicHold.h diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp index e2cbdac425..cf5dad8fa5 100644 --- a/interface/src/InterfaceActionFactory.cpp +++ b/interface/src/InterfaceActionFactory.cpp @@ -12,7 +12,6 @@ #include -#include #include #include @@ -29,8 +28,6 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); case ACTION_TYPE_HOLD: return (EntityActionPointer) new AvatarActionHold(id, ownerEntity); - case ACTION_TYPE_KINEMATIC_HOLD: - return (EntityActionPointer) new AvatarActionKinematicHold(id, ownerEntity); } assert(false); diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 815293bf0d..bdd2a9c08a 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -75,12 +75,54 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { _active = true; }); if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); + if (_kinematic) { + doKinematicUpdate(deltaTimeStep); + } else { + ObjectActionSpring::updateActionWorker(deltaTimeStep); + } } } } } +void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + qDebug() << "AvatarActionHold::doKinematicUpdate -- no owning entity"; + return; + } + void* physicsInfo = ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + qDebug() << "AvatarActionHold::doKinematicUpdate -- no owning physics info"; + return; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr; + if (!rigidBody) { + qDebug() << "AvatarActionHold::doKinematicUpdate -- no rigidBody"; + return; + } + + withWriteLock([&]{ + if (_kinematicSetVelocity) { + if (_previousSet) { + glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); + // back up along velocity a bit in order to smooth out a "vibrating" appearance + _positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f; + } + } + + btTransform worldTrans = rigidBody->getWorldTransform(); + worldTrans.setOrigin(glmToBullet(_positionalTarget)); + worldTrans.setRotation(glmToBullet(_rotationalTarget)); + rigidBody->setWorldTransform(worldTrans); + + _previousPositionalTarget = _positionalTarget; + _previousRotationalTarget = _rotationalTarget; + _previousSet = true; + }); +} bool AvatarActionHold::updateArguments(QVariantMap arguments) { glm::vec3 relativePosition; @@ -88,6 +130,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { float timeScale; QString hand; QUuid holderID; + bool kinematic; + bool kinematicSetVelocity; bool needUpdate = false; bool somethingChanged = ObjectAction::updateArguments(arguments); @@ -120,12 +164,27 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { auto myAvatar = DependencyManager::get()->getMyAvatar(); holderID = myAvatar->getSessionUUID(); + ok = true; + kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false); + if (!ok) { + _kinematic = false; + } + + ok = true; + kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments, + "kinematic-set-velocity", ok, false); + if (!ok) { + _kinematicSetVelocity = false; + } + if (somethingChanged || relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale || hand != _hand || - holderID != _holderID) { + holderID != _holderID || + kinematic != _kinematic || + kinematicSetVelocity != _kinematicSetVelocity) { needUpdate = true; } }); @@ -139,6 +198,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { _angularTimeScale = _linearTimeScale; _hand = hand; _holderID = holderID; + _kinematic = kinematic; + _kinematicSetVelocity = kinematicSetVelocity; _active = true; auto ownerEntity = _ownerEntity.lock(); diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 3cc70ced09..042e6a0f25 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -39,6 +39,13 @@ private: glm::quat _relativeRotation; QString _hand; QUuid _holderID; + + void doKinematicUpdate(float deltaTimeStep); + bool _kinematic = false; + bool _kinematicSetVelocity = false; + bool _previousSet = false; + glm::vec3 _previousPositionalTarget; + glm::quat _previousRotationalTarget; }; #endif // hifi_AvatarActionHold_h diff --git a/interface/src/avatar/AvatarActionKinematicHold.cpp b/interface/src/avatar/AvatarActionKinematicHold.cpp deleted file mode 100644 index 680363877f..0000000000 --- a/interface/src/avatar/AvatarActionKinematicHold.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// -// AvatarActionKinematicHold.cpp -// interface/src/avatar/ -// -// Created by Seth Alves 2015-6-9 -// 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 "QVariantGLM.h" -#include "avatar/MyAvatar.h" -#include "avatar/AvatarManager.h" - -#include "AvatarActionKinematicHold.h" - -const uint16_t AvatarActionKinematicHold::holdVersion = 1; - -AvatarActionKinematicHold::AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity) : - ObjectActionSpring(id, ownerEntity), - _relativePosition(glm::vec3(0.0f)), - _relativeRotation(glm::quat()), - _hand("right"), - _mine(false), - _previousPositionalTarget(Vectors::ZERO), - _previousRotationalTarget(Quaternions::IDENTITY) -{ - _type = ACTION_TYPE_KINEMATIC_HOLD; - #if WANT_DEBUG - qDebug() << "AvatarActionKinematicHold::AvatarActionKinematicHold"; - #endif -} - -AvatarActionKinematicHold::~AvatarActionKinematicHold() { - #if WANT_DEBUG - qDebug() << "AvatarActionKinematicHold::~AvatarActionKinematicHold"; - #endif -} - -void AvatarActionKinematicHold::updateActionWorker(float deltaTimeStep) { - if (!_mine) { - // if a local script isn't updating this, then we are just getting spring-action data over the wire. - // let the super-class handle it. - ObjectActionSpring::updateActionWorker(deltaTimeStep); - return; - } - - assert(deltaTimeStep > 0.0f); - - glm::quat rotation; - glm::vec3 position; - glm::vec3 offset; - bool gotLock = withTryReadLock([&]{ - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = myAvatar->getRightPalmPosition(); - palmRotation = myAvatar->getRightPalmRotation(); - } else { - palmPosition = myAvatar->getLeftPalmPosition(); - palmRotation = myAvatar->getLeftPalmRotation(); - } - - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; - }); - - if (gotLock) { - gotLock = withTryWriteLock([&]{ - if (_positionalTarget != position || _rotationalTarget != rotation) { - _positionalTarget = position; - _rotationalTarget = rotation; - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - void* physicsInfo = ownerEntity->getPhysicsInfo(); - if (physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr; - if (!rigidBody) { - qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; - return; - } - - if (_setVelocity) { - if (_previousSet) { - glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; - rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); - // back up along velocity a bit in order to smooth out a "vibrating" appearance - _positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f; - } - } - - btTransform worldTrans = rigidBody->getWorldTransform(); - worldTrans.setOrigin(glmToBullet(_positionalTarget)); - worldTrans.setRotation(glmToBullet(_rotationalTarget)); - rigidBody->setWorldTransform(worldTrans); - - _previousPositionalTarget = _positionalTarget; - _previousRotationalTarget = _rotationalTarget; - _previousSet = true; - } - } - } - }); - } - - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); - } -} - - -bool AvatarActionKinematicHold::updateArguments(QVariantMap arguments) { - if (!ObjectAction::updateArguments(arguments)) { - return false; - } - bool ok = true; - glm::vec3 relativePosition = - EntityActionInterface::extractVec3Argument("kinematic-hold", arguments, "relativePosition", ok, false); - if (!ok) { - relativePosition = _relativePosition; - } - - ok = true; - glm::quat relativeRotation = - EntityActionInterface::extractQuatArgument("kinematic-hold", arguments, "relativeRotation", ok, false); - if (!ok) { - relativeRotation = _relativeRotation; - } - - ok = true; - QString hand = - EntityActionInterface::extractStringArgument("kinematic-hold", arguments, "hand", ok, false); - if (!ok || !(hand == "left" || hand == "right")) { - hand = _hand; - } - - ok = true; - int setVelocity = - EntityActionInterface::extractIntegerArgument("kinematic-hold", arguments, "setVelocity", ok, false); - if (!ok) { - setVelocity = false; - } - - if (relativePosition != _relativePosition - || relativeRotation != _relativeRotation - || hand != _hand) { - withWriteLock([&] { - _relativePosition = relativePosition; - _relativeRotation = relativeRotation; - _hand = hand; - _setVelocity = setVelocity; - - _mine = true; - _active = true; - activateBody(); - }); - } - return true; -} - - -QVariantMap AvatarActionKinematicHold::getArguments() { - QVariantMap arguments = ObjectAction::getArguments(); - withReadLock([&]{ - if (!_mine) { - arguments = ObjectActionSpring::getArguments(); - return; - } - - arguments["relativePosition"] = glmToQMap(_relativePosition); - arguments["relativeRotation"] = glmToQMap(_relativeRotation); - arguments["setVelocity"] = _setVelocity; - arguments["hand"] = _hand; - }); - return arguments; -} - - -void AvatarActionKinematicHold::deserialize(QByteArray serializedArguments) { - if (!_mine) { - ObjectActionSpring::deserialize(serializedArguments); - } -} diff --git a/interface/src/avatar/AvatarActionKinematicHold.h b/interface/src/avatar/AvatarActionKinematicHold.h deleted file mode 100644 index b4fceab1e7..0000000000 --- a/interface/src/avatar/AvatarActionKinematicHold.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// AvatarActionKinematicHold.h -// interface/src/avatar/ -// -// Created by Seth Alves 2015-10-2 -// 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_AvatarActionKinematicHold_h -#define hifi_AvatarActionKinematicHold_h - -#include - -#include -#include - -class AvatarActionKinematicHold : public ObjectActionSpring { -public: - AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity); - virtual ~AvatarActionKinematicHold(); - - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); - - virtual void updateActionWorker(float deltaTimeStep); - - virtual void deserialize(QByteArray serializedArguments); - -private: - static const uint16_t holdVersion; - - glm::vec3 _relativePosition; - glm::quat _relativeRotation; - QString _hand; - bool _mine = false; - - bool _previousSet = false; - glm::vec3 _previousPositionalTarget; - glm::quat _previousRotationalTarget; - - bool _setVelocity = false; -}; - -#endif // hifi_AvatarActionKinematicHold_h diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index d874646b4b..3e88c61e49 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -100,9 +100,6 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "hold") { return ACTION_TYPE_HOLD; } - if (normalizedActionTypeString == "kinematichold") { - return ACTION_TYPE_KINEMATIC_HOLD; - } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; @@ -118,8 +115,6 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "spring"; case ACTION_TYPE_HOLD: return "hold"; - case ACTION_TYPE_KINEMATIC_HOLD: - return "kinematic-hold"; } assert(false); return "none"; @@ -286,6 +281,23 @@ QString EntityActionInterface::extractStringArgument(QString objectName, QVarian return v; } +bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return false; + } + + QVariant vV = arguments[argumentName]; + bool v = vV.toBool(); + return v; +} + + + QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType) { return stream << (quint16)entityActionType; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 49739e7da9..d97b5e8106 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -23,8 +23,7 @@ enum EntityActionType { ACTION_TYPE_NONE = 0, ACTION_TYPE_OFFSET = 1000, ACTION_TYPE_SPRING = 2000, - ACTION_TYPE_HOLD = 3000, - ACTION_TYPE_KINEMATIC_HOLD = 4000 + ACTION_TYPE_HOLD = 3000 }; @@ -66,6 +65,8 @@ public: QString argumentName, bool& ok, bool required = true); static QString extractStringArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); + static bool extractBooleanArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); protected: virtual glm::vec3 getPosition() = 0; From 937959c1c1ea19e82c90b58558ffe481b01bc069 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Oct 2015 09:57:27 -0700 Subject: [PATCH 0182/1003] cleanups --- libraries/entities/src/EntityActionInterface.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index 3e88c61e49..549aacbd0a 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -275,10 +275,7 @@ QString EntityActionInterface::extractStringArgument(QString objectName, QVarian ok = false; return ""; } - - QVariant vV = arguments[argumentName]; - QString v = vV.toString(); - return v; + return arguments[argumentName].toString(); } bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments, @@ -290,10 +287,7 @@ bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantM ok = false; return false; } - - QVariant vV = arguments[argumentName]; - bool v = vV.toBool(); - return v; + return arguments[argumentName].toBool(); } From 966499b9e5e5edf7d07a4477a34605b0607e1318 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Oct 2015 10:17:17 -0700 Subject: [PATCH 0183/1003] continually activate (in the bullet sense) a held object --- interface/src/avatar/AvatarActionHold.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index bdd2a9c08a..6568f8932e 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -78,6 +78,7 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { if (_kinematic) { doKinematicUpdate(deltaTimeStep); } else { + activateBody(); ObjectActionSpring::updateActionWorker(deltaTimeStep); } } From f25cc93936229efc139bd4e5b318cb22643353b5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 16 Oct 2015 10:48:36 -0700 Subject: [PATCH 0184/1003] 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 fe709a9397951ae67e7e9f224fa03a6c03bea1b4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Oct 2015 10:53:33 -0700 Subject: [PATCH 0185/1003] keep object active in kinematic mode, also --- interface/src/avatar/AvatarActionHold.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 6568f8932e..0264777164 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -123,6 +123,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { _previousRotationalTarget = _rotationalTarget; _previousSet = true; }); + + activateBody(); } bool AvatarActionHold::updateArguments(QVariantMap arguments) { From 066696e201b5738d95ca8fee7257a6c0b93c6ff7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Oct 2015 11:18:41 -0700 Subject: [PATCH 0186/1003] don't go into continue-far-grabbing-non-colliding mode. --- examples/controllers/handControllerGrab.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 44aa1146d4..c607946f2f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -393,7 +393,8 @@ function MyController(hand, triggerAction) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. - var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); + var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * + DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); // how far did avatar move this timestep? var currentPosition = MyAvatar.position; @@ -492,7 +493,8 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - lifetime: ACTION_LIFETIME + lifetime: ACTION_LIFETIME, + // kinematic: true, }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -544,7 +546,8 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - lifetime: ACTION_LIFETIME + lifetime: ACTION_LIFETIME, + // kinematic: true }); this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); } @@ -576,7 +579,9 @@ function MyController(hand, triggerAction) { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } Entities.callEntityMethod(this.grabbedEntity, "startFarGrabNonColliding"); - this.setState(STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING); + // TODO -- figure out how to make this work. + // this.setState(STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING); + this.setState(STATE_RELEASE); }; this.continueNearGrabbingNonColliding = function() { From c98685f6d390d7d6e1f2d09a3b3fae60c6a0673b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 16 Oct 2015 11:39:27 -0700 Subject: [PATCH 0187/1003] in STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING, when the ray moves off of the entity, switch back to search state --- examples/controllers/handControllerGrab.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index c607946f2f..faa90efdff 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -579,9 +579,7 @@ function MyController(hand, triggerAction) { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } Entities.callEntityMethod(this.grabbedEntity, "startFarGrabNonColliding"); - // TODO -- figure out how to make this work. - // this.setState(STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING); - this.setState(STATE_RELEASE); + this.setState(STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING); }; this.continueNearGrabbingNonColliding = function() { @@ -589,7 +587,7 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } - + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; @@ -605,6 +603,16 @@ function MyController(hand, triggerAction) { direction: Quat.getUp(this.getHandRotation()) }; + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + var intersection = Entities.findRayIntersection(pickRay, true); + this.lastPickTime = now; + if (intersection.entityID != this.grabbedEntity) { + this.setState(STATE_RELEASE); + return; + } + } + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); Entities.callEntityMethod(this.grabbedEntity, "continueFarGrabbingNonColliding"); }; From 4b21bdf3087aac01dc02121d3f519ad49198a66b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 16 Oct 2015 11:50:13 -0700 Subject: [PATCH 0188/1003] testing --- .../AC_scripts/originalPositionResetter.js | 217 ++ unpublishedScripts/hiddenEntityReset.js | 2464 ++++++++--------- unpublishedScripts/masterReset.js | 1359 +++++---- 3 files changed, 2004 insertions(+), 2036 deletions(-) create mode 100644 examples/toys/AC_scripts/originalPositionResetter.js diff --git a/examples/toys/AC_scripts/originalPositionResetter.js b/examples/toys/AC_scripts/originalPositionResetter.js new file mode 100644 index 0000000000..d3112732f7 --- /dev/null +++ b/examples/toys/AC_scripts/originalPositionResetter.js @@ -0,0 +1,217 @@ +// +// originalPositionResetter.js +// toybox +// +// Created by James B. Pollack @imgntn 10/16/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 +// +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var TARGET_MODEL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target.fbx"; +var TARGET_COLLISION_HULL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target_collision_hull.obj"; +var TARGET_DIMENSIONS = { + x: 0.06, + y: 0.42, + z: 0.42 +}; +var TARGET_ROTATION = Quat.fromPitchYawRollDegrees(0, -55.25, 0); + +var targetsScriptURL = Script.resolvePath('../ping_pong_gun/wallTarget.js'); + + +var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; + +var NUMBER_OF_BALLS = 4; +var BALL_DIAMETER = 0.30; +var RESET_DISTANCE = 1; +var MINIMUM_MOVE_LENGTH = 0.05; + +var totalTime = 0; +var lastUpdate = 0; +var UPDATE_INTERVAL = 1 / 5; // 5fps + +var Resetter = { + searchForEntitiesToResetToOriginalPosition: function(searchOrigin, objectName) { + var ids = Entities.findEntities(searchOrigin, 5); + var objects = []; + var i; + var entityID; + var name; + for (i = 0; i < ids.length; i++) { + entityID = ids[i]; + name = Entities.getEntityProperties(entityID, "name").name; + if (name === objectName) { + //we found an object to reset + objects.push(entityID); + } + } + return objects; + }, + deleteObjects: function(objects) { + while (objects.length > 0) { + Entities.deleteEntity(objects.pop()); + } + }, + createBasketBalls: function() { + var position = { + x: 542.86, + y: 494.84, + z: 475.06 + }; + var i; + var ballPosition; + var collidingBall; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + ballPosition = { + x: position.x, + y: position.y + BALL_DIAMETER * 2, + z: position.z + (BALL_DIAMETER) - (BALL_DIAMETER * i) + }; + + collidingBall = Entities.addEntity({ + type: "Model", + name: 'Hifi-Basketball', + shapeType: 'Sphere', + position: ballPosition, + dimensions: { + x: BALL_DIAMETER, + y: BALL_DIAMETER, + z: BALL_DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + modelURL: basketballURL, + userData: JSON.stringify({ + originalPositionKey: { + originalPosition: ballPosition + }, + resetMe: { + resetMe: true + }, + grabbable: { + invertSolidWhileHeld: true + } + }) + }); + + } + }, + testBallDistanceFromStart: function(balls) { + var resetCount = 0; + balls.forEach(function(ball, index) { + var properties = Entities.getEntityProperties(ball, ["position", "userData"]); + var currentPosition = properties.position; + var originalPosition = properties.userData.originalPositionKey.originalPosition; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + if (length > RESET_DISTANCE) { + Script.setTimeout(function() { + var newPosition = Entities.getEntityProperties(ball, "position").position; + var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); + if (moving < MINIMUM_MOVE_LENGTH) { + if (resetCount === balls.length) { + this.deleteObjects(balls); + this.createBasketBalls(); + } + } + }, 200); + } + }); + }, + testTargetDistanceFromStart: function(targets) { + targets.forEach(function(target, index) { + var properties = Entities.getEntityProperties(target, ["position", "userData"]); + var currentPosition = properties.position; + var originalPosition = properties.userData.originalPositionKey.originalPosition; + var distance = Vec3.subtract(originalPosition, currentPosition); + var length = Vec3.length(distance); + if (length > RESET_DISTANCE) { + Script.setTimeout(function() { + var newPosition = Entities.getEntityProperties(target, "position").position; + var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); + if (moving < MINIMUM_MOVE_LENGTH) { + + Entities.deleteEntity(target); + + var targetProperties = { + name: 'Hifi-Target', + type: 'Model', + modelURL: TARGET_MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: TARGET_COLLISION_HULL_URL, + position: originalPosition, + rotation: TARGET_ROTATION, + script: targetsScriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + }, + originalPositionKey: originalPosition + + }) + }; + + Entities.addEntity(targetProperties); + } + }, 200); + } + + }); + + } +}; + +function update(deltaTime) { + + if (!Entities.serversExist() || !Entities.canRez()) { + return; + } + + + totalTime += deltaTime; + + // We don't want to edit the entity EVERY update cycle, because that's just a lot + // of wasted bandwidth and extra effort on the server for very little visual gain + if (totalTime - lastUpdate > UPDATE_INTERVAL) { + //do stuff + var balls = Resetter.searchForEntitiesToResetToOriginalPosition({ + x: 542.86, + y: 494.84, + z: 475.06 + }, "Hifi-Basketball"); + + var targets = Resetter.searchForEntitiesToResetToOriginalPosition({ + x: 548.68, + y: 497.30, + z: 509.74 + }, "Hifi-Target"); + + if (balls.length !== 0) { + Resetter.testBallDistanceFromStart(balls); + } + + if (targets.length !== 0) { + Resetter.testTargetDistanceFromStart(targets); + } + + lastUpdate = totalTime; + } + +} + +Script.update.connect(update); \ No newline at end of file diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 13e5f89914..42056f6c3b 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -12,1341 +12,1219 @@ (function() { - var _this; + var _this; - var sprayPaintScriptURL = Script.resolvePath("../examples/toys/sprayPaintCan.js"); - var catScriptURL = Script.resolvePath("../examples/toys/cat.js"); - var flashlightScriptURL = Script.resolvePath('../examples/toys/flashlight/flashlight.js'); - var pingPongScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/pingPongGun.js'); - var wandScriptURL = Script.resolvePath("../examples/toys/bubblewand/wand.js"); - var dollScriptURL = Script.resolvePath("../examples/toys/doll/doll.js"); - var lightsScriptURL = Script.resolvePath("../examples/toys/lightSwitch.js"); - var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTarget.js'); + var sprayPaintScriptURL = Script.resolvePath("../examples/toys/sprayPaintCan.js"); + var catScriptURL = Script.resolvePath("../examples/toys/cat.js"); + var flashlightScriptURL = Script.resolvePath('../examples/toys/flashlight/flashlight.js'); + var pingPongScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/pingPongGun.js'); + var wandScriptURL = Script.resolvePath("../examples/toys/bubblewand/wand.js"); + var dollScriptURL = Script.resolvePath("../examples/toys/doll/doll.js"); + var lightsScriptURL = Script.resolvePath("../examples/toys/lightSwitch.js"); + var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTarget.js'); - ResetSwitch = function() { - _this = this; - }; + ResetSwitch = function() { + _this = this; + }; - ResetSwitch.prototype = { + ResetSwitch.prototype = { - clickReleaseOnEntity: function(entityId, mouseEvent) { - if (!mouseEvent.isLeftButton) { - return; - } - this.triggerReset(); + clickReleaseOnEntity: function(entityId, mouseEvent) { + if (!mouseEvent.isLeftButton) { + return; + } + this.triggerReset(); - }, + }, - startNearGrabNonColliding: function() { - this.triggerReset(); - }, + startNearGrabNonColliding: function() { + this.triggerReset(); + }, - triggerReset: function() { - MasterReset(); - }, + triggerReset: function() { + MasterReset(); + }, - preload: function(entityID) { - this.entityID = entityID; - } - - }; - - - MasterReset = function() { - var resetKey = "resetMe"; - var GRABBABLE_DATA_KEY = "grabbableKey"; - - var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - - var shouldDeleteOnEndScript = false; - - //Before creating anything, first search a radius and delete all the things that should be deleted - deleteAllToys(); - createAllToys(); - - function createAllToys() { - createBlocks({ - x: 548.3, - y: 495.55, - z: 504.4 - }); - - createBasketBall({ - x: 547.73, - y: 495.5, - z: 505.47 - }); - - createDoll({ - x: 546.67, - y: 495.41, - z: 505.09 - }); - - createWand({ - x: 546.71, - y: 495.55, - z: 506.15 - }); - - createDice(); - - createFlashlight({ - x: 545.72, - y: 495.41, - z: 505.78 - }); - - - - createCombinedArmChair({ - x: 549.29, - y: 494.9, - z: 508.22 - }); - - createPottedPlant({ - x: 554.26, - y: 495.2, - z: 504.53 - }); - - createPingPongBallGun(); - - createBasketballHoop(); - createBasketballRack(); - - createGates(); - - createFire(); - // Handles toggling of all sconce lights - createLights(); - - - - createCat({ - x: 551.09, - y: 494.98, - z: 503.49 - }); - - - createSprayCan({ - x: 549.7, - y: 495.6, - z: 503.91 - }); - - createTargets(); - - } - - function deleteAllToys() { - var entities = Entities.findEntities(MyAvatar.position, 100); - - entities.forEach(function(entity) { - //params: customKey, id, defaultValue - var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; - if (shouldReset === true) { - Entities.deleteEntity(entity); + preload: function(entityID) { + this.entityID = entityID; } - }); - } - function createFire() { + }; - var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0); + MasterReset = function() { + var resetKey = "resetMe"; + var GRABBABLE_DATA_KEY = "grabbableKey"; - var animationSettings = JSON.stringify({ - fps: 30, - running: true, - loop: true, - firstFrame: 1, - lastFrame: 10000 - }); + var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + + var shouldDeleteOnEndScript = false; + + //Before creating anything, first search a radius and delete all the things that should be deleted + deleteAllToys(); + createAllToys(); + + function createAllToys() { + createBlocks({ + x: 548.3, + y: 495.55, + z: 504.4 + }); + + createBasketBall({ + x: 547.73, + y: 495.5, + z: 505.47 + }); + + createDoll({ + x: 546.67, + y: 495.41, + z: 505.09 + }); + + createWand({ + x: 546.71, + y: 495.55, + z: 506.15 + }); + + createDice(); + + createFlashlight({ + x: 545.72, + y: 495.41, + z: 505.78 + }); - var fire = Entities.addEntity({ - type: "ParticleEffect", - name: "fire", - animationSettings: animationSettings, - textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", - position: { - x: 551.45, - y: 494.82, - z: 502.05 - }, - emitRate: 100, - colorStart: { - red: 70, - green: 70, - blue: 137 - }, - color: { - red: 200, - green: 99, - blue: 42 - }, - colorFinish: { - red: 255, - green: 99, - blue: 32 - }, - radiusSpread: 0.01, - radiusStart: 0.02, - radiusEnd: 0.001, - particleRadius: 0.05, - radiusFinish: 0.0, - emitOrientation: myOrientation, - emitSpeed: 0.3, - speedSpread: 0.1, - alphaStart: 0.05, - alpha: 0.1, - alphaFinish: 0.05, - emitDimensions: { - x: 1, - y: 1, - z: 0.1 - }, - polarFinish: 0.1, - emitAcceleration: { - x: 0.0, - y: 0.0, - z: 0.0 - }, - accelerationSpread: { - x: 0.1, - y: 0.01, - z: 0.1 - }, - lifespan: 1, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - } - function createBasketballRack() { - var NUMBER_OF_BALLS = 4; - var DIAMETER = 0.30; - var RESET_DISTANCE = 1; - var MINIMUM_MOVE_LENGTH = 0.05; - var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; - var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; - var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; - var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; + createCombinedArmChair({ + x: 549.29, + y: 494.9, + z: 508.22 + }); - var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); + createPottedPlant({ + x: 554.26, + y: 495.2, + z: 504.53 + }); - var rackStartPosition = { - x: 542.86, - y: 494.84, - z: 475.06 - }; - var rack = Entities.addEntity({ - name: 'Basketball Rack', - type: "Model", - modelURL: rackURL, - position: rackStartPosition, - rotation: rackRotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - linearDamping: 1, - dimensions: { - x: 0.4, - y: 1.37, - z: 1.73 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - compoundShapeURL: rackCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } + createPingPongBallGun(); - }) - }); + createBasketballHoop(); + createBasketballRack(); - var collidingBalls = []; - var originalBallPositions = []; + createGates(); - function createCollidingBalls() { - var position = rackStartPosition; + createFire(); + // Handles toggling of all sconce lights + createLights(); - var i; - for (i = 0; i < NUMBER_OF_BALLS; i++) { - var ballPosition = { - x: position.x, - y: position.y + DIAMETER * 2, - z: position.z + (DIAMETER) - (DIAMETER * i) - }; - var collidingBall = Entities.addEntity({ - type: "Model", - name: 'Colliding Basketball', - shapeType: 'Sphere', - position: { - x: position.x + (DIAMETER * 2) - (DIAMETER * i), - y: position.y + DIAMETER * 2, - z: position.z - }, - dimensions: { - x: DIAMETER, - y: DIAMETER, - z: DIAMETER - }, - restitution: 1.0, - linearDamping: 0.00001, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - modelURL: basketballURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }); - collidingBalls.push(collidingBall); - originalBallPositions.push(position); + createCat({ + x: 551.09, + y: 494.98, + z: 503.49 + }); + + + createSprayCan({ + x: 549.7, + y: 495.6, + z: 503.91 + }); + + createTargets(); + } - } - function testBallDistanceFromStart() { - var resetCount = 0; + function deleteAllToys() { + var entities = Entities.findEntities(MyAvatar.position, 100); - collidingBalls.forEach(function(ball, index) { - var currentPosition = Entities.getEntityProperties(ball, "position").position; - var originalPosition = originalBallPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); - - if (length > RESET_DISTANCE) { - Script.setTimeout(function() { - var newPosition = Entities.getEntityProperties(ball, "position").position; - var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); - if (moving < MINIMUM_MOVE_LENGTH) { - resetCount++; - if (resetCount === NUMBER_OF_BALLS) { - deleteCollidingBalls(); - createCollidingBalls(); + entities.forEach(function(entity) { + //params: customKey, id, defaultValue + var shouldReset = getEntityCustomData(resetKey, entity, {}).resetMe; + if (shouldReset === true) { + Entities.deleteEntity(entity); } - } - }, 200); - } - }); - } - - function deleteEntity(entityID) { - if (entityID === rack) { - deleteCollidingBalls(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); + }); } - } - function deleteCollidingBalls() { - while (collidingBalls.length > 0) { - Entities.deleteEntity(collidingBalls.pop()); - } - } + function createFire() { - createCollidingBalls(); - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); - } + var myOrientation = Quat.fromPitchYawRollDegrees(-90, 0, 0.0); - function createTargets() { + var animationSettings = JSON.stringify({ + fps: 30, + running: true, + loop: true, + firstFrame: 1, + lastFrame: 10000 + }); - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; - var MINIMUM_MOVE_LENGTH = 0.05; - var RESET_DISTANCE = 0.5; - var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; - var NUMBER_OF_TARGETS = 6; - var TARGETS_PER_ROW = 3; - - var TARGET_DIMENSIONS = { - x: 0.06, - y: 0.42, - z: 0.42 - }; - - var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; - var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; - - var startPosition = { - x: 548.68, - y: 497.30, - z: 509.74 - }; - - var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); - - var targetIntervalClearer = Entities.addEntity({ - name: 'Target Interval Clearer - delete me to clear', - type: 'Box', - position: startPosition, - dimensions: TARGET_DIMENSIONS, - rotation: rotation, - visible: false, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }); - - var targets = []; - - var originalPositions = []; - - var lastPositions = []; - - function addTargets() { - var i; - var row = -1; - for (i = 0; i < NUMBER_OF_TARGETS; i++) { - - if (i % TARGETS_PER_ROW === 0) { - row++; - } - - var vHat = Quat.getFront(rotation); - var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); - var multiplier = Vec3.multiply(spacer, vHat); - var position = Vec3.sum(startPosition, multiplier); - position.y = startPosition.y - (row * VERTICAL_SPACING); - - originalPositions.push(position); - lastPositions.push(position); - - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: position, - rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }; - var target = Entities.addEntity(targetProperties); - targets.push(target); - } - } - - function testTargetDistanceFromStart() { - targets.forEach(function(target, index) { - - var currentPosition = Entities.getEntityProperties(target, "position").position; - var originalPosition = originalPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); - - var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); - - lastPositions[index] = currentPosition; - - if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { - - Entities.deleteEntity(target); - - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: originalPositions[index], - rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true + var fire = Entities.addEntity({ + type: "ParticleEffect", + name: "fire", + animationSettings: animationSettings, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + position: { + x: 551.45, + y: 494.82, + z: 502.05 }, - grabbableKey: { - grabbable: false + emitRate: 100, + colorStart: { + red: 70, + green: 70, + blue: 137 + }, + color: { + red: 200, + green: 99, + blue: 42 + }, + colorFinish: { + red: 255, + green: 99, + blue: 32 + }, + radiusSpread: 0.01, + radiusStart: 0.02, + radiusEnd: 0.001, + particleRadius: 0.05, + radiusFinish: 0.0, + emitOrientation: myOrientation, + emitSpeed: 0.3, + speedSpread: 0.1, + alphaStart: 0.05, + alpha: 0.1, + alphaFinish: 0.05, + emitDimensions: { + x: 1, + y: 1, + z: 0.1 + }, + polarFinish: 0.1, + emitAcceleration: { + x: 0.0, + y: 0.0, + z: 0.0 + }, + accelerationSpread: { + x: 0.1, + y: 0.01, + z: 0.1 + }, + lifespan: 1, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + } + + function createBasketballRack() { + var NUMBER_OF_BALLS = 4; + var DIAMETER = 0.30; + var RESET_DISTANCE = 1; + var MINIMUM_MOVE_LENGTH = 0.05; + var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; + var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + var rackURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/basketball_rack.fbx"; + var rackCollisionHullURL = HIFI_PUBLIC_BUCKET + "models/basketball_hoop/rack_collision_hull.obj"; + + var rackRotation = Quat.fromPitchYawRollDegrees(0, -90, 0); + + var rackStartPosition = { + x: 542.86, + y: 494.84, + z: 475.06 + }; + var rack = Entities.addEntity({ + name: 'Basketball Rack', + type: "Model", + modelURL: rackURL, + position: rackStartPosition, + rotation: rackRotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + linearDamping: 1, + dimensions: { + x: 0.4, + y: 1.37, + z: 1.73 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + compoundShapeURL: rackCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + + var collidingBalls = []; + + function createCollidingBalls() { + var position = rackStartPosition; + + var i; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + var ballPosition = { + x: position.x, + y: position.y + DIAMETER * 2, + z: position.z + (DIAMETER) - (DIAMETER * i) + }; + var newPosition = { + x: position.x + (DIAMETER * 2) - (DIAMETER * i), + y: position.y + DIAMETER * 2, + z: position.z + }; + var collidingBall = Entities.addEntity({ + type: "Model", + name: 'Hifi-Basketball', + shapeType: 'Sphere', + position: newPosition, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + ignoreForCollisions: false, + modelURL: basketballURL, + userData: JSON.stringify({ + originalPositionKey: { + originalPosition: newPosition + }, + resetMe: { + resetMe: true + }, + grabbable: { + invertSolidWhileHeld: true + } + }) + }); + + collidingBalls.push(collidingBall); + } - }) + } + + createCollidingBalls(); + + } + + function createTargets() { + + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; + + var MINIMUM_MOVE_LENGTH = 0.05; + var RESET_DISTANCE = 0.5; + var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; + var NUMBER_OF_TARGETS = 6; + var TARGETS_PER_ROW = 3; + + var TARGET_DIMENSIONS = { + x: 0.06, + y: 0.42, + z: 0.42 }; - targets[index] = Entities.addEntity(targetProperties); - - } - }); - } - - - function deleteEntity(entityID) { - if (entityID === targetIntervalClearer) { - deleteTargets(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteTargets() { - while (targets.length > 0) { - Entities.deleteEntity(targets.pop()); - } - Entities.deleteEntity(targetIntervalClearer); - } - - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); - - addTargets(); - - } - - function createCat(position) { - - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; - var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; - var animationSettings = JSON.stringify({ - running: true, - }); - var cat = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "cat", - script: catScriptURL, - animationURL: animationURL, - animationSettings: animationSettings, - position: position, - rotation: { - w: 0.35020983219146729, - x: -4.57763671875e-05, - y: 0.93664455413818359, - z: -1.52587890625e-05 - }, - dimensions: { - x: 0.15723302960395813, - y: 0.50762706995010376, - z: 0.90716040134429932 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }); - - } - - function createFlashlight(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - - var flashlight = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "flashlight", - script: flashlightScriptURL, - position: position, - dimensions: { - x: 0.08, - y: 0.30, - z: 0.08 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: 'box', - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - invertSolidWhileHeld: true - } - - }) - }); - - - } - - function createLights() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; - - - var rotation = { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: lightsScriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Hall Light" - } - }) - }); - - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - rotation = { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }; - axis = { - x: 0, - y: 1, - z: 0 - }; - dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: lightsScriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Garage Light" - } - }) - }); - - - - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, - - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - } - - - - function createDice() { - var diceProps = { - type: "Model", - modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", - name: "dice", - position: { - x: 541, - y: 494.96, - z: 509.1 - }, - dimensions: { - x: 0.09, - y: 0.09, - z: 0.09 - }, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: "box", - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }; - var dice1 = Entities.addEntity(diceProps); - - diceProps.position = { - x: 541.05, - y: 494.96, - z: 509.0 - }; - - var dice2 = Entities.addEntity(diceProps); - - } - - - function createGates() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; - - var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); - var gate = Entities.addEntity({ - name: 'Front Door Fence', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'box', - position: { - x: 531.15, - y: 495.11, - z: 520.20 - }, - dimensions: { - x: 1.42, - y: 1.13, - z: 0.2 - }, - rotation: rotation, - collisionsWillMove: true, - gravity: { - x: 0, - y: -100, - z: 0 - }, - linearDamping: 1, - angularDamping: 10, - mass: 10, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - function createPingPongBallGun() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - - var position = { - x: 548.6, - y: 495.4, - z: 503.39 - }; - - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); - - var pingPongGun = Entities.addEntity({ - type: "Model", - modelURL: MODEL_URL, - shapeType: 'box', - script: pingPongScriptURL, - position: position, - rotation: rotation, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 0.08, - y: 0.21, - z: 0.47 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }); - } - - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - - var entity = Entities.addEntity({ - name: 'Bubble Wand', - type: "Model", - modelURL: WAND_MODEL, - position: position, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 0.05, - y: 0.25, - z: 0.05 - }, - //must be enabled to be grabbable in the physics engine - shapeType: 'compound', - collisionsWillMove: true, - compoundShapeURL: WAND_COLLISION_SHAPE, - //Look into why bubble wand is going through table when gravity is enabled - // gravity: {x: 0, y: -3.5, z: 0}, - // velocity: {x: 0, y: -0.01, z:0}, - script: wandScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }); - - - } - - function createBasketBall(position) { - - var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: position, - collisionsWillMove: true, - shapeType: "sphere", - name: "basketball", - dimensions: { - x: 0.25, - y: 0.26, - z: 0.25 - }, - gravity: { - x: 0, - y: -7, - z: 0 - }, - restitution: 10, - linearDamping: 0.0, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }); - - } - - function createDoll(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; - - var naturalDimensions = { - x: 1.63, - y: 1.67, - z: 0.26 - }; - var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - var entity = Entities.addEntity({ - type: "Model", - name: "doll", - modelURL: modelURL, - script: dollScriptURL, - position: position, - shapeType: 'box', - dimensions: desiredDimensions, - gravity: { - x: 0, - y: -5, - z: 0 - }, - velocity: { - x: 0, - y: -0.1, - z: 0 - }, - collisionsWillMove: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }); - - } - - function createSprayCan(position) { - - var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "spraycan", - script: sprayPaintScriptURL, - modelURL: modelURL, - position: position, - dimensions: { - x: 0.07, - y: 0.17, - z: 0.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -0.5, - z: 0 - }, - velocity: { - x: 0, - y: -1, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } - }) - }); - - } - - function createPottedPlant(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; - - var entity = Entities.addEntity({ - type: "Model", - name: "Potted Plant", - modelURL: modelURL, - position: position, - dimensions: { - x: 1.10, - y: 2.18, - z: 1.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.4, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - - - function createCombinedArmChair(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; - var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; - - var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); - - var entity = Entities.addEntity({ - type: "Model", - name: "Red Arm Chair", - modelURL: modelURL, - shapeType: 'compound', - compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, - position: position, - rotation: rotation, - dimensions: { - x: 1.26, - y: 1.56, - z: 1.35 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -0.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.2, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); - - } - - function createBlocks(position) { - var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; - var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; - var NUM_BLOCKS_PER_COLOR = 4; - var i, j; - - var blockTypes = [{ - url: "planky_blue.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_green.fbx", - dimensions: { - x: 0.1, - y: 0.1, - z: 0.25 - } - }, { - url: "planky_natural.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.05 - } - }, { - url: "planky_yellow.fbx", - dimensions: { - x: 0.03, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_red.fbx", - dimensions: { - x: 0.1, - y: 0.05, - z: 0.25 - } - }, ]; - - var modelURL, entity; - for (i = 0; i < blockTypes.length; i++) { - for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { - modelURL = baseURL + blockTypes[i].url; - entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: Vec3.sum(position, { - x: j / 10, - y: i / 10, - z: 0 - }), - shapeType: 'box', - name: "block", - dimensions: blockTypes[i].dimensions, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - gravity: { - x: 0, - y: -2.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); + var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; + var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; + + + var startPosition = { + x: 548.68, + y: 497.30, + z: 509.74 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); + + var targets = []; + + function addTargets() { + var i; + var row = -1; + for (i = 0; i < NUMBER_OF_TARGETS; i++) { + + if (i % TARGETS_PER_ROW === 0) { + row++; + } + + var vHat = Quat.getFront(rotation); + var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); + var multiplier = Vec3.multiply(spacer, vHat); + var position = Vec3.sum(startPosition, multiplier); + position.y = startPosition.y - (row * VERTICAL_SPACING); + + var targetProperties = { + name: 'Hifi-Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: position, + rotation: rotation, + script: targetsScriptURL, + userData: JSON.stringify({ + originalPositionKey: { + originalPosition: position + }, + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }; + + var target = Entities.addEntity(targetProperties); + targets.push(target); + + } + } + + addTargets(); } - } - } - function cleanup() { - deleteAllToys(); - } + function createCat(position) { - if (shouldDeleteOnEndScript) { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; + var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; + var animationSettings = JSON.stringify({ + running: true + }); + var cat = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "cat", + script: catScriptURL, + animationURL: animationURL, + animationSettings: animationSettings, + position: position, + rotation: { + w: 0.35020983219146729, + x: -4.57763671875e-05, + y: 0.93664455413818359, + z: -1.52587890625e-05 + }, + dimensions: { + x: 0.15723302960395813, + y: 0.50762706995010376, + z: 0.90716040134429932 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); - Script.scriptEnding.connect(cleanup); - } - }; - // entity scripts always need to return a newly constructed object of our type - return new ResetSwitch(); + } + + function createFlashlight(position) { + var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; + + var flashlight = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "flashlight", + script: flashlightScriptURL, + position: position, + dimensions: { + x: 0.08, + y: 0.30, + z: 0.08 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: 'box', + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + + }) + }); + + + } + + function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; + + + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { + x: 0, + y: 1, + z: 0 + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: lightsScriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Hall Light" + } + }) + }); + + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); + + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); + + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: lightsScriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Garage Light" + } + }) + }); + + + + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, + + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + } + + + + function createDice() { + var diceProps = { + type: "Model", + modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", + name: "dice", + position: { + x: 541, + y: 494.96, + z: 509.1 + }, + dimensions: { + x: 0.09, + y: 0.09, + z: 0.09 + }, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: "box", + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }; + var dice1 = Entities.addEntity(diceProps); + + diceProps.position = { + x: 541.05, + y: 494.96, + z: 509.0 + }; + + var dice2 = Entities.addEntity(diceProps); + + } + + + function createGates() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + + var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); + var gate = Entities.addEntity({ + name: 'Front Door Fence', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'box', + position: { + x: 531.15, + y: 495.11, + z: 520.20 + }, + dimensions: { + x: 1.42, + y: 1.13, + z: 0.2 + }, + rotation: rotation, + collisionsWillMove: true, + gravity: { + x: 0, + y: -100, + z: 0 + }, + linearDamping: 1, + angularDamping: 10, + mass: 10, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createPingPongBallGun() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + + var position = { + x: 548.6, + y: 495.4, + z: 503.39 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + + var pingPongGun = Entities.addEntity({ + type: "Model", + modelURL: MODEL_URL, + shapeType: 'box', + script: pingPongScriptURL, + position: position, + rotation: rotation, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.08, + y: 0.21, + z: 0.47 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + } + + function createBasketballHoop() { + var position = { + x: 539.23, + y: 496.13, + z: 475.89 + }; + var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); + + var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; + var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + + var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: position, + rotation: rotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createWand(position) { + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; + + var entity = Entities.addEntity({ + name: 'Bubble Wand', + type: "Model", + modelURL: WAND_MODEL, + position: position, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 0.05, + y: 0.25, + z: 0.05 + }, + //must be enabled to be grabbable in the physics engine + shapeType: 'compound', + collisionsWillMove: true, + compoundShapeURL: WAND_COLLISION_SHAPE, + //Look into why bubble wand is going through table when gravity is enabled + // gravity: {x: 0, y: -3.5, z: 0}, + // velocity: {x: 0, y: -0.01, z:0}, + script: wandScriptURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + + + } + + function createBasketBall(position) { + + var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: position, + collisionsWillMove: true, + shapeType: "sphere", + name: "basketball", + dimensions: { + x: 0.25, + y: 0.26, + z: 0.25 + }, + gravity: { + x: 0, + y: -7, + z: 0 + }, + restitution: 10, + linearDamping: 0.0, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + + } + + function createDoll(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; + + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + var entity = Entities.addEntity({ + type: "Model", + name: "doll", + modelURL: modelURL, + script: dollScriptURL, + position: position, + shapeType: 'box', + dimensions: desiredDimensions, + gravity: { + x: 0, + y: -5, + z: 0 + }, + velocity: { + x: 0, + y: -0.1, + z: 0 + }, + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + + } + + function createSprayCan(position) { + + var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "spraycan", + script: sprayPaintScriptURL, + modelURL: modelURL, + position: position, + dimensions: { + x: 0.07, + y: 0.17, + z: 0.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -0.5, + z: 0 + }, + velocity: { + x: 0, + y: -1, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + + } + + function createPottedPlant(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; + + var entity = Entities.addEntity({ + type: "Model", + name: "Potted Plant", + modelURL: modelURL, + position: position, + dimensions: { + x: 1.10, + y: 2.18, + z: 1.07 + }, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.4, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + + function createCombinedArmChair(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; + var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; + + var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); + + var entity = Entities.addEntity({ + type: "Model", + name: "Red Arm Chair", + modelURL: modelURL, + shapeType: 'compound', + compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, + position: position, + rotation: rotation, + dimensions: { + x: 1.26, + y: 1.56, + z: 1.35 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -0.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.2, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + + } + + function createBlocks(position) { + var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var NUM_BLOCKS_PER_COLOR = 4; + var i, j; + + var blockTypes = [{ + url: "planky_blue.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_green.fbx", + dimensions: { + x: 0.1, + y: 0.1, + z: 0.25 + } + }, { + url: "planky_natural.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + } + }, { + url: "planky_yellow.fbx", + dimensions: { + x: 0.03, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_red.fbx", + dimensions: { + x: 0.1, + y: 0.05, + z: 0.25 + } + } ]; + + var modelURL, entity; + for (i = 0; i < blockTypes.length; i++) { + for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { + modelURL = baseURL + blockTypes[i].url; + entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: Vec3.sum(position, { + x: j / 10, + y: i / 10, + z: 0 + }), + shapeType: 'box', + name: "block", + dimensions: blockTypes[i].dimensions, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + gravity: { + x: 0, + y: -2.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + + } + } + } + + function cleanup() { + deleteAllToys(); + } + + if (shouldDeleteOnEndScript) { + + Script.scriptEnding.connect(cleanup); + } + }; + // entity scripts always need to return a newly constructed object of our type + return new ResetSwitch(); }); \ No newline at end of file diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index f855caa9a1..2ecd52f344 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -251,7 +251,6 @@ MasterReset = function() { }); var collidingBalls = []; - var originalBallPositions = []; function createCollidingBalls() { var position = rackStartPosition; @@ -263,16 +262,16 @@ MasterReset = function() { y: position.y + DIAMETER * 2, z: position.z + (DIAMETER) - (DIAMETER * i) }; - + var newPosition = { + x: position.x + (DIAMETER * 2) - (DIAMETER * i), + y: position.y + DIAMETER * 2, + z: position.z + }; var collidingBall = Entities.addEntity({ type: "Model", - name: 'Colliding Basketball', + name: 'Hifi-Basketball', shapeType: 'Sphere', - position: { - x: position.x + (DIAMETER * 2) - (DIAMETER * i), - y: position.y + DIAMETER * 2, - z: position.z - }, + position: newPosition, dimensions: { x: DIAMETER, y: DIAMETER, @@ -289,6 +288,9 @@ MasterReset = function() { ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ + originalPositionKey: { + originalPosition: newPosition + }, resetMe: { resetMe: true }, @@ -299,53 +301,12 @@ MasterReset = function() { }); collidingBalls.push(collidingBall); - originalBallPositions.push(position); - } - } - function testBallDistanceFromStart() { - var resetCount = 0; - - collidingBalls.forEach(function(ball, index) { - var currentPosition = Entities.getEntityProperties(ball, "position").position; - var originalPosition = originalBallPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); - - if (length > RESET_DISTANCE) { - Script.setTimeout(function() { - var newPosition = Entities.getEntityProperties(ball, "position").position; - var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); - if (moving < MINIMUM_MOVE_LENGTH) { - resetCount++; - if (resetCount === NUMBER_OF_BALLS) { - deleteCollidingBalls(); - createCollidingBalls(); - } - } - }, 200); - } - }); - } - - function deleteEntity(entityID) { - if (entityID === rack) { - deleteCollidingBalls(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteCollidingBalls() { - while (collidingBalls.length > 0) { - Entities.deleteEntity(collidingBalls.pop()); } } createCollidingBalls(); - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); } function createTargets() { @@ -377,27 +338,8 @@ MasterReset = function() { var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); - var targetIntervalClearer = Entities.addEntity({ - name: 'Target Interval Clearer - delete me to clear', - type: 'Box', - position: startPosition, - dimensions: TARGET_DIMENSIONS, - rotation: rotation, - visible: false, - collisionsWillMove: false, - ignoreForCollisions: true, - userData: JSON.stringify({ - resetMe: { - resetMe: true - } - }) - }); - var targets = []; - var originalPositions = []; - var lastPositions = []; - function addTargets() { var i; var row = -1; @@ -413,11 +355,8 @@ MasterReset = function() { var position = Vec3.sum(startPosition, multiplier); position.y = startPosition.y - (row * VERTICAL_SPACING); - originalPositions.push(position); - lastPositions.push(position); - var targetProperties = { - name: 'Target', + name: 'Hifi-Target', type: 'Model', modelURL: MODEL_URL, shapeType: 'compound', @@ -428,6 +367,9 @@ MasterReset = function() { rotation: rotation, script: targetsScriptURL, userData: JSON.stringify({ + originalPositionKey: { + originalPosition: position + }, resetMe: { resetMe: true }, @@ -443,81 +385,18 @@ MasterReset = function() { } } - function testTargetDistanceFromStart() { - targets.forEach(function(target, index) { + addTargets(); - var currentPosition = Entities.getEntityProperties(target, "position").position; - var originalPosition = originalPositions[index]; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); - - var moving = Vec3.length(Vec3.subtract(currentPosition, lastPositions[index])); - - lastPositions[index] = currentPosition; - - if (length > RESET_DISTANCE && moving < MINIMUM_MOVE_LENGTH) { - - Entities.deleteEntity(target); - - var targetProperties = { - name: 'Target', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: COLLISION_HULL_URL, - position: originalPositions[index], - rotation: rotation, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - } - }) - }; - - targets[index] = Entities.addEntity(targetProperties); - - } - }); } + function createCat(position) { - function deleteEntity(entityID) { - if (entityID === targetIntervalClearer) { - deleteTargets(); - Script.clearInterval(distanceCheckInterval); - Entities.deletingEntity.disconnect(deleteEntity); - } - } - - function deleteTargets() { - while (targets.length > 0) { - Entities.deleteEntity(targets.pop()); - } - Entities.deleteEntity(targetIntervalClearer); - } - - Entities.deletingEntity.connect(deleteEntity); - var distanceCheckInterval = Script.setInterval(testTargetDistanceFromStart, 1000); - - addTargets(); - -} - -function createCat(position) { - - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; - var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; - var animationSettings = JSON.stringify({ - running: true, - }); - var cat = Entities.addEntity({ + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/Dark_Cat.fbx"; + var animationURL = "http://hifi-public.s3.amazonaws.com/ryan/sleeping.fbx"; + var animationSettings = JSON.stringify({ + running: true + }); + var cat = Entities.addEntity({ type: "Model", modelURL: modelURL, name: "cat", @@ -537,22 +416,21 @@ function createCat(position) { z: 0.90716040134429932 }, userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false } }) - }); + }); -} + } -function createFlashlight(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; + function createFlashlight(position) { + var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - var flashlight = Entities.addEntity({ + var flashlight = Entities.addEntity({ type: "Model", modelURL: modelURL, name: "flashlight", @@ -576,353 +454,352 @@ function createFlashlight(position) { }, shapeType: 'box', userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - invertSolidWhileHeld: true - } - } - }) - }); - - -} - -function createLights() { - var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; - - - var rotation = { - w: 0.63280689716339111, - x: 0.63280689716339111, - y: -0.31551080942153931, - z: 0.31548023223876953 - }; - var axis = { - x: 0, - y: 1, - z: 0 - }; - var dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchHall = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Hall", - script: lightsScriptURL, - position: { - x: 543.27764892578125, - y: 495.67999267578125, - z: 511.00564575195312 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Hall Light" - } - }) - }); - - var sconceLight1 = Entities.addEntity({ - type: "Light", - position: { - x: 543.75, - y: 496.24, - z: 511.13 - }, - name: "Sconce 1 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - var sconceLight2 = Entities.addEntity({ - type: "Light", - position: { - x: 540.1, - y: 496.24, - z: 505.57 - }, - name: "Sconce 2 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Hall Light" - } - }) - }); - - rotation = { - w: 0.20082402229309082, - x: 0.20082402229309082, - y: -0.67800414562225342, - z: 0.67797362804412842 - }; - axis = { - x: 0, - y: 1, - z: 0 - }; - dQ = Quat.angleAxis(180, axis); - rotation = Quat.multiply(rotation, dQ); - - var lightSwitchGarage = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - name: "Light Switch Garage", - script: lightsScriptURL, - position: { - x: 545.62, - y: 495.68, - z: 500.21 - }, - rotation: rotation, - dimensions: { - x: 0.10546875, - y: 0.032372996211051941, - z: 0.16242524981498718 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - on: true, - type: "Garage Light" - } - }) - }); - - - - var sconceLight3 = Entities.addEntity({ - type: "Light", - position: { - x: 545.49468994140625, - y: 496.24026489257812, - z: 500.63516235351562 - }, - - name: "Sconce 3 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - - var sconceLight4 = Entities.addEntity({ - type: "Light", - position: { - x: 550.90399169921875, - y: 496.24026489257812, - z: 507.90237426757812 - }, - name: "Sconce 4 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - - var sconceLight5 = Entities.addEntity({ - type: "Light", - position: { - x: 548.407958984375, - y: 496.24026489257812, - z: 509.5504150390625 - }, - name: "Sconce 5 Light", - dimensions: { - x: 2.545, - y: 2.545, - z: 2.545 - }, - cutoff: 90, - color: { - red: 217, - green: 146, - blue: 24 - }, - isSpotlight: false, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - type: "Garage Light" - } - }) - }); - -} - - - -function createDice() { - var diceProps = { - type: "Model", - modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", - collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", - name: "dice", - position: { - x: 541, - y: 494.96, - z: 509.1 - }, - dimensions: { - x: 0.09, - y: 0.09, - z: 0.09 - }, - gravity: { - x: 0, - y: -3.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - shapeType: "box", - collisionsWillMove: true, - userData: JSON.stringify({ resetMe: { - resetMe: true, + resetMe: true }, grabbableKey: { invertSolidWhileHeld: true } - } - }) -}; -var dice1 = Entities.addEntity(diceProps); - -diceProps.position = { - x: 541.05, - y: 494.96, - z: 509.0 -}; - -var dice2 = Entities.addEntity(diceProps); - -} + }) + }); -function createGates() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + } - var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); - var gate = Entities.addEntity({ - name: 'Front Door Fence', - type: 'Model', - modelURL: MODEL_URL, - shapeType: 'box', - position: { - x: 531.15, - y: 495.11, - z: 520.20 - }, - dimensions: { - x: 1.42, - y: 1.13, - z: 0.2 - }, - rotation: rotation, - collisionsWillMove: true, - gravity: { + function createLights() { + var modelURL = "http://hifi-public.s3.amazonaws.com/ryan/lightswitch.fbx"; + + + var rotation = { + w: 0.63280689716339111, + x: 0.63280689716339111, + y: -0.31551080942153931, + z: 0.31548023223876953 + }; + var axis = { x: 0, - y: -100, + y: 1, z: 0 - }, - linearDamping: 1, - angularDamping: 10, - mass: 10, - userData: JSON.stringify({ - resetMe: { - resetMe: true, + }; + var dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); + + var lightSwitchHall = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Hall", + script: lightsScriptURL, + position: { + x: 543.27764892578125, + y: 495.67999267578125, + z: 511.00564575195312 }, - grabbableKey: { - grabbable: false - } - }) - }); -} + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Hall Light" + } + }) + }); -function createPingPongBallGun() { - var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + var sconceLight1 = Entities.addEntity({ + type: "Light", + position: { + x: 543.75, + y: 496.24, + z: 511.13 + }, + name: "Sconce 1 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); - var position = { - x: 548.6, - y: 495.4, - z: 503.39 - }; + var sconceLight2 = Entities.addEntity({ + type: "Light", + position: { + x: 540.1, + y: 496.24, + z: 505.57 + }, + name: "Sconce 2 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Hall Light" + } + }) + }); - var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + rotation = { + w: 0.20082402229309082, + x: 0.20082402229309082, + y: -0.67800414562225342, + z: 0.67797362804412842 + }; + axis = { + x: 0, + y: 1, + z: 0 + }; + dQ = Quat.angleAxis(180, axis); + rotation = Quat.multiply(rotation, dQ); - var pingPongGun = Entities.addEntity({ + var lightSwitchGarage = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + name: "Light Switch Garage", + script: lightsScriptURL, + position: { + x: 545.62, + y: 495.68, + z: 500.21 + }, + rotation: rotation, + dimensions: { + x: 0.10546875, + y: 0.032372996211051941, + z: 0.16242524981498718 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + on: true, + type: "Garage Light" + } + }) + }); + + + + var sconceLight3 = Entities.addEntity({ + type: "Light", + position: { + x: 545.49468994140625, + y: 496.24026489257812, + z: 500.63516235351562 + }, + + name: "Sconce 3 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + + var sconceLight4 = Entities.addEntity({ + type: "Light", + position: { + x: 550.90399169921875, + y: 496.24026489257812, + z: 507.90237426757812 + }, + name: "Sconce 4 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + var sconceLight5 = Entities.addEntity({ + type: "Light", + position: { + x: 548.407958984375, + y: 496.24026489257812, + z: 509.5504150390625 + }, + name: "Sconce 5 Light", + dimensions: { + x: 2.545, + y: 2.545, + z: 2.545 + }, + cutoff: 90, + color: { + red: 217, + green: 146, + blue: 24 + }, + isSpotlight: false, + userData: JSON.stringify({ + resetMe: { + resetMe: true, + type: "Garage Light" + } + }) + }); + + } + + + + function createDice() { + var diceProps = { + type: "Model", + modelURL: "http://s3.amazonaws.com/hifi-public/models/props/Dice/goldDie.fbx", + collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/dice/diceCollide.wav", + name: "dice", + position: { + x: 541, + y: 494.96, + z: 509.1 + }, + dimensions: { + x: 0.09, + y: 0.09, + z: 0.09 + }, + gravity: { + x: 0, + y: -3.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + shapeType: "box", + collisionsWillMove: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + + }) + }; + var dice1 = Entities.addEntity(diceProps); + + diceProps.position = { + x: 541.05, + y: 494.96, + z: 509.0 + }; + + var dice2 = Entities.addEntity(diceProps); + + } + + + function createGates() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/ryan/fence.fbx'; + + var rotation = Quat.fromPitchYawRollDegrees(0, -16, 0); + var gate = Entities.addEntity({ + name: 'Front Door Fence', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'box', + position: { + x: 531.15, + y: 495.11, + z: 520.20 + }, + dimensions: { + x: 1.42, + y: 1.13, + z: 0.2 + }, + rotation: rotation, + collisionsWillMove: true, + gravity: { + x: 0, + y: -100, + z: 0 + }, + linearDamping: 1, + angularDamping: 10, + mass: 10, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + function createPingPongBallGun() { + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; + + var position = { + x: 548.6, + y: 495.4, + z: 503.39 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 36, 0); + + var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, shapeType: 'box', @@ -941,61 +818,61 @@ function createPingPongBallGun() { }, collisionsWillMove: true, userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + + }) + }); + } + + function createBasketballHoop() { + var position = { + x: 539.23, + y: 496.13, + z: 475.89 + }; + var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); + + var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; + var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + + var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: position, + rotation: rotation, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false } }) - }); -} + }); + } -function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); + function createWand(position) { + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - grabbable: false - } - }) - }); -} - -function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ name: 'Bubble Wand', type: "Model", modelURL: WAND_MODEL, @@ -1019,24 +896,23 @@ function createWand(position) { // velocity: {x: 0, y: -0.01, z:0}, script: wandScriptURL, userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } }) - }); + }); -} + } -function createBasketBall(position) { + function createBasketBall(position) { - var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; + var modelURL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ type: "Model", modelURL: modelURL, position: position, @@ -1062,28 +938,27 @@ function createBasketBall(position) { }, collisionSoundURL: "http://s3.amazonaws.com/hifi-public/sounds/basketball/basketball.wav", userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } }) - }); + }); -} + } -function createDoll(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; + function createDoll(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx"; - var naturalDimensions = { - x: 1.63, - y: 1.67, - z: 0.26 - }; - var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); - var entity = Entities.addEntity({ + var naturalDimensions = { + x: 1.63, + y: 1.67, + z: 0.26 + }; + var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15); + var entity = Entities.addEntity({ type: "Model", name: "doll", modelURL: modelURL, @@ -1103,23 +978,22 @@ function createDoll(position) { }, collisionsWillMove: true, userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } }) - }); + }); -} + } -function createSprayCan(position) { + function createSprayCan(position) { - var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; + var modelURL = "https://hifi-public.s3.amazonaws.com/eric/models/paintcan.fbx"; - var entity = Entities.addEntity({ + var entity = Entities.addEntity({ type: "Model", name: "spraycan", script: sprayPaintScriptURL, @@ -1143,186 +1017,185 @@ function createSprayCan(position) { z: 0 }, userData: JSON.stringify({ - resetMe: { - resetMe: true, - }, - grabbableKey: { - invertSolidWhileHeld: true - } + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true } }) - }); + }); -} + } -function createPottedPlant(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; + function createPottedPlant(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/potted_plant/potted_plant.fbx"; - var entity = Entities.addEntity({ - type: "Model", - name: "Potted Plant", - modelURL: modelURL, - position: position, - dimensions: { - x: 1.10, - y: 2.18, - z: 1.07 - }, - collisionsWillMove: true, - shapeType: 'box', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.4, - userData: JSON.stringify({ - resetMe: { - resetMe: true, + var entity = Entities.addEntity({ + type: "Model", + name: "Potted Plant", + modelURL: modelURL, + position: position, + dimensions: { + x: 1.10, + y: 2.18, + z: 1.07 }, - grabbableKey: { - grabbable: false - } - }) - }); -} - - -function createCombinedArmChair(position) { - var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; - var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; - - var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); - - var entity = Entities.addEntity({ - type: "Model", - name: "Red Arm Chair", - modelURL: modelURL, - shapeType: 'compound', - compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, - position: position, - rotation: rotation, - dimensions: { - x: 1.26, - y: 1.56, - z: 1.35 - }, - collisionsWillMove: true, - gravity: { - x: 0, - y: -0.8, - z: 0 - }, - velocity: { - x: 0, - y: 0, - z: 0 - }, - linearDamping: 0.2, - userData: JSON.stringify({ - resetMe: { - resetMe: true, + collisionsWillMove: true, + shapeType: 'box', + gravity: { + x: 0, + y: -9.8, + z: 0 }, - grabbableKey: { - grabbable: false + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.4, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + } + + + function createCombinedArmChair(position) { + var modelURL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/combined_chair.fbx"; + var RED_ARM_CHAIR_COLLISION_HULL = "http://hifi-public.s3.amazonaws.com/models/red_arm_chair/red_arm_chair_collision_hull.obj"; + + var rotation = Quat.fromPitchYawRollDegrees(0, -143, 0); + + var entity = Entities.addEntity({ + type: "Model", + name: "Red Arm Chair", + modelURL: modelURL, + shapeType: 'compound', + compoundShapeURL: RED_ARM_CHAIR_COLLISION_HULL, + position: position, + rotation: rotation, + dimensions: { + x: 1.26, + y: 1.56, + z: 1.35 + }, + collisionsWillMove: true, + gravity: { + x: 0, + y: -0.8, + z: 0 + }, + velocity: { + x: 0, + y: 0, + z: 0 + }, + linearDamping: 0.2, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }); + + } + + function createBlocks(position) { + var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var NUM_BLOCKS_PER_COLOR = 4; + var i, j; + + var blockTypes = [{ + url: "planky_blue.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.25 } - }) - }); + }, { + url: "planky_green.fbx", + dimensions: { + x: 0.1, + y: 0.1, + z: 0.25 + } + }, { + url: "planky_natural.fbx", + dimensions: { + x: 0.05, + y: 0.05, + z: 0.05 + } + }, { + url: "planky_yellow.fbx", + dimensions: { + x: 0.03, + y: 0.05, + z: 0.25 + } + }, { + url: "planky_red.fbx", + dimensions: { + x: 0.1, + y: 0.05, + z: 0.25 + } + }]; -} - -function createBlocks(position) { - var baseURL = HIFI_PUBLIC_BUCKET + "models/content/planky/"; - var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; - var NUM_BLOCKS_PER_COLOR = 4; - var i, j; - - var blockTypes = [{ - url: "planky_blue.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_green.fbx", - dimensions: { - x: 0.1, - y: 0.1, - z: 0.25 - } - }, { - url: "planky_natural.fbx", - dimensions: { - x: 0.05, - y: 0.05, - z: 0.05 - } - }, { - url: "planky_yellow.fbx", - dimensions: { - x: 0.03, - y: 0.05, - z: 0.25 - } - }, { - url: "planky_red.fbx", - dimensions: { - x: 0.1, - y: 0.05, - z: 0.25 - } - }, ]; - - var modelURL, entity; - for (i = 0; i < blockTypes.length; i++) { - for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { - modelURL = baseURL + blockTypes[i].url; - entity = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: Vec3.sum(position, { - x: j / 10, - y: i / 10, - z: 0 - }), - shapeType: 'box', - name: "block", - dimensions: blockTypes[i].dimensions, - collisionsWillMove: true, - collisionSoundURL: collisionSoundURL, - gravity: { - x: 0, - y: -2.5, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - userData: JSON.stringify({ - resetMe: { - resetMe: true, - } - }) - }); + var modelURL, entity; + for (i = 0; i < blockTypes.length; i++) { + for (j = 0; j < NUM_BLOCKS_PER_COLOR; j++) { + modelURL = baseURL + blockTypes[i].url; + entity = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: Vec3.sum(position, { + x: j / 10, + y: i / 10, + z: 0 + }), + shapeType: 'box', + name: "block", + dimensions: blockTypes[i].dimensions, + collisionsWillMove: true, + collisionSoundURL: collisionSoundURL, + gravity: { + x: 0, + y: -2.5, + z: 0 + }, + velocity: { + x: 0, + y: -0.01, + z: 0 + }, + userData: JSON.stringify({ + resetMe: { + resetMe: true + } + }) + }); + } } } -} -function cleanup() { - deleteAllToys(); -} + function cleanup() { + deleteAllToys(); + } -if (shouldDeleteOnEndScript) { + if (shouldDeleteOnEndScript) { - Script.scriptEnding.connect(cleanup); -} + Script.scriptEnding.connect(cleanup); + } }; \ No newline at end of file From f0034844e7b5ba938b7234239f2f2683f99fd917 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Oct 2015 12:24:12 -0700 Subject: [PATCH 0189/1003] 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 0190/1003] 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 0191/1003] 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 0192/1003] 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 8c22db17cccae48fd0b8c17391bf00d01036622a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 16 Oct 2015 15:43:23 -0700 Subject: [PATCH 0193/1003] Fix reading of QVector from models files - polyline strokeWidths --- libraries/shared/src/VariantMapToScriptValue.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp index c0d21e9995..00fc2cd682 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -62,12 +62,11 @@ QScriptValue variantMapToScriptValue(QVariantMap& variantMap, QScriptEngine& scr QScriptValue variantListToScriptValue(QVariantList& variantList, QScriptEngine& scriptEngine) { - QScriptValue scriptValue = scriptEngine.newObject(); - scriptValue.setProperty("length", variantList.size()); - int i = 0; - foreach (QVariant v, variantList) { - scriptValue.setProperty(i++, variantToScriptValue(v, scriptEngine)); + QScriptValue scriptValue = scriptEngine.newArray(); + + for (int i = 0; i < variantList.size(); i++) { + scriptValue.setProperty(i, variantToScriptValue(variantList[i], scriptEngine)); } return scriptValue; From aa6aaf912e3d12b0e94dc3c106314361fc2cfe24 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 16 Oct 2015 16:07:20 -0700 Subject: [PATCH 0194/1003] add color bars below position inputs --- examples/html/entityProperties.html | 6 +++--- examples/html/style.css | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 97d8fe4729..9fa596ae09 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -1035,9 +1035,9 @@
Position
-
X
-
Y
-
Z
+
X
+
Y
+
Z
diff --git a/examples/html/style.css b/examples/html/style.css index 3614ea821b..98a3e8bd6e 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -75,6 +75,7 @@ body { height: 22.5pt; } + .property-section label { font-weight: bold; } @@ -342,3 +343,23 @@ input#property-name { margin: 5px; border: 1px solid white; } + +.prop-x{ + color:red !important; + background: rgba(255, 0, 0, .9); + height:2px !important; + +} + +.prop-y{ + color:green !important; + background: rgba(0, 255, 0, .9); + height:2px !important; + +} + +.prop-z{ + color:blue !important; + background: rgba(0, 0, 255, .9); + height:2px !important; +} From c6fcaf158b221fd934fdc3861366783aa6114e4f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 16 Oct 2015 16:15:20 -0700 Subject: [PATCH 0195/1003] add color to more props --- examples/html/entityProperties.html | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 9fa596ae09..cffca523d7 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -1048,18 +1048,18 @@
Registration
-
X
-
Y
-
Z
+
X
+
Y
+
Z
Dimensions
-
X
-
Y
-
Z
+
X
+
Y
+
Z
@@ -1123,9 +1123,9 @@
Linear Velocity
-
X
-
Y
-
Z
+
X
+
Y
+
Z
@@ -1164,18 +1164,18 @@
Gravity
-
X
-
Y
-
Z
+
X
+
Y
+
Z
Acceleration
-
X
-
Y
-
Z
+
X
+
Y
+
Z
From 38a967ac5493ba53940f8b768a0808d22f77b0f2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 16 Oct 2015 16:28:11 -0700 Subject: [PATCH 0196/1003] 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 31cc451fd44e38e608b3377842a9d79ae94427e8 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 16 Oct 2015 16:42:29 -0700 Subject: [PATCH 0197/1003] css cleanup --- examples/html/style.css | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/html/style.css b/examples/html/style.css index 98a3e8bd6e..5b794cbfec 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -344,18 +344,16 @@ input#property-name { border: 1px solid white; } -.prop-x{ - color:red !important; - background: rgba(255, 0, 0, .9); - height:2px !important; - +.prop-x { + color:red !important; + background: rgba(255, 0, 0, .9); + height:2px !important; } -.prop-y{ +.prop-y { color:green !important; background: rgba(0, 255, 0, .9); height:2px !important; - } .prop-z{ From 1dcf03d61e7627f17389eeba635920693a79e248 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 16 Oct 2015 17:15:46 -0700 Subject: [PATCH 0198/1003] 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 0199/1003] 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 0200/1003] 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 1da68e02146056ce922d853db8a68c6ea7a86820 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 16 Oct 2015 18:10:50 -0700 Subject: [PATCH 0201/1003] Fix particle bug by having lifetimes increase --- libraries/entities/src/ParticleEffectEntityItem.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 29e2aa512d..ddd79375b3 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -644,14 +644,13 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { // update particles between head and tail for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) { - _particleLifetimes[i] -= deltaTime; + _particleLifetimes[i] += deltaTime; // if particle has died. - if (_particleLifetimes[i] <= 0.0f || _lifespan == 0.0f) { + if (_particleLifetimes[i] >= _lifespan || _lifespan < EPSILON) { // move head forward _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; - } - else { + } else { float age = 1.0f - _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0 updateRadius(i, age); updateColor(i, age); @@ -672,7 +671,7 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { // emit a new particle at tail index. quint32 i = _particleTailIndex; - _particleLifetimes[i] = _lifespan; + _particleLifetimes[i] = 0.0f; // Radius if (_radiusSpread == 0.0f) { From f90f0ed1f342c37b2e8187981f10022a8021a425 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 17 Oct 2015 12:34:32 -0700 Subject: [PATCH 0202/1003] 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 0203/1003] 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 0204/1003] 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 0205/1003] 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 0206/1003] 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 0207/1003] 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 0208/1003] 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 0209/1003] 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 1f3b92577d15054948c5f9198900728879732565 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 17 Oct 2015 20:08:25 -0700 Subject: [PATCH 0210/1003] update _model's location anytime it doesn't match the entity's location --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8a973c88a9..bfed81a944 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -298,7 +298,11 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } bool movingOrAnimating = isMoving() || isAnimatingSomething(); - if ((movingOrAnimating || _needsInitialSimulation) && _model->isActive() && _dimensionsInitialized) { + if ((movingOrAnimating || + _needsInitialSimulation || + _model->getTranslation() != getPosition() || + _model->getRotation() != getRotation()) + && _model->isActive() && _dimensionsInitialized) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); From 734a39f962916e49dfeaa0702a0d13f57d4c1c6d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 18 Oct 2015 18:58:03 -0700 Subject: [PATCH 0211/1003] 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 0212/1003] 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 0213/1003] 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 82c041d06a20d9e244e05e528f7d3060be9ea033 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 18 Oct 2015 22:52:20 -0700 Subject: [PATCH 0214/1003] Adding git attributes file --- .gitattributes | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..5a6cc5e815 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,25 @@ +*.cpp text +*.c text +*.h text +*.qml text +*.js text +*.json text +*.slv text +*.slf text +*.slh text +*.vert text +*.frag text +*.fst text +*.ini text +*.html text +*.ts text +*.txt text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.wav binary +*.fbx text +*.dds text +*.svg text +*.ttf text From 282cdadf6381eb1d21f14072e3b269883c3e4170 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 18 Oct 2015 22:55:14 -0700 Subject: [PATCH 0215/1003] Fixing line endings --- .../src/DeferredLightingEffect.cpp | 1788 +++++++-------- .../render-utils/src/RenderDeferredTask.cpp | 6 +- libraries/render-utils/src/Skinning.slh | 138 +- .../directional_ambient_light_shadow_map.slf | 2 +- .../src/directional_light_shadow_map.slf | 2 +- .../directional_skybox_light_shadow_map.slf | 2 +- .../render-utils/src/model_normal_map.slv | 88 +- libraries/render-utils/src/model_shadow.slv | 52 +- libraries/render-utils/src/skin_model.slv | 96 +- .../src/skin_model_normal_map.slv | 112 +- .../render-utils/src/skin_model_shadow.slv | 58 +- libraries/render/src/render/DrawStatus.cpp | 326 +-- libraries/render/src/render/DrawStatus.h | 86 +- .../render/src/render/drawItemBounds.slv | 70 +- .../render/src/render/drawItemStatus.slv | 204 +- .../src/SceneScriptingInterface.cpp | 4 +- tools/scribe/src/TextTemplate.cpp | 1978 ++++++++--------- tools/scribe/src/TextTemplate.h | 392 ++-- 18 files changed, 2702 insertions(+), 2702 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index e9d2cf4061..57e3129ef8 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -1,894 +1,894 @@ -// -// DeferredLightingEffect.cpp -// interface/src/renderer -// -// Created by Andrzej Kapolka on 9/11/14. -// 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 "DeferredLightingEffect.h" - -#include -#include -#include - -#include -#include -#include - -#include "AbstractViewStateInterface.h" -#include "GeometryCache.h" -#include "TextureCache.h" -#include "FramebufferCache.h" - - -#include "simple_vert.h" -#include "simple_textured_frag.h" -#include "simple_textured_emisive_frag.h" - -#include "deferred_light_vert.h" -#include "deferred_light_limited_vert.h" -#include "deferred_light_spot_vert.h" - -#include "directional_light_frag.h" -#include "directional_light_shadow_map_frag.h" -#include "directional_light_cascaded_shadow_map_frag.h" - -#include "directional_ambient_light_frag.h" -#include "directional_ambient_light_shadow_map_frag.h" -#include "directional_ambient_light_cascaded_shadow_map_frag.h" - -#include "directional_skybox_light_frag.h" -#include "directional_skybox_light_shadow_map_frag.h" -#include "directional_skybox_light_cascaded_shadow_map_frag.h" - -#include "point_light_frag.h" -#include "spot_light_frag.h" - -static const std::string glowIntensityShaderHandle = "glowIntensity"; - -struct LightLocations { - int shadowDistances; - int shadowScale; - int radius; - int ambientSphere; - int lightBufferUnit; - int atmosphereBufferUnit; - int texcoordMat; - int coneParam; - int deferredTransformBuffer; -}; - -static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); - - -gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config) { - auto it = _simplePrograms.find(config); - if (it != _simplePrograms.end()) { - return it.value(); - } - - auto state = std::make_shared(); - if (config.isCulled()) { - state->setCullMode(gpu::State::CULL_BACK); - } else { - state->setCullMode(gpu::State::CULL_NONE); - } - state->setDepthTest(true, true, gpu::LESS_EQUAL); - if (config.hasDepthBias()) { - state->setDepthBias(1.0f); - state->setDepthBiasSlopeScale(1.0f); - } - state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; - gpu::PipelinePointer pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); - _simplePrograms.insert(config, pipeline); - return pipeline; -} - -void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { - auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert))); - auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag))); - auto PSEmissive = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_emisive_frag))); - - _simpleShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); - _emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive)); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); - gpu::Shader::makeProgram(*_simpleShader, slotBindings); - gpu::Shader::makeProgram(*_emissiveShader, slotBindings); - - _viewState = viewState; - _directionalLightLocations = std::make_shared(); - _directionalLightShadowMapLocations = std::make_shared(); - _directionalLightCascadedShadowMapLocations = std::make_shared(); - _directionalAmbientSphereLightLocations = std::make_shared(); - _directionalAmbientSphereLightShadowMapLocations = std::make_shared(); - _directionalAmbientSphereLightCascadedShadowMapLocations = std::make_shared(); - _directionalSkyboxLightLocations = std::make_shared(); - _directionalSkyboxLightShadowMapLocations = std::make_shared(); - _directionalSkyboxLightCascadedShadowMapLocations = std::make_shared(); - _pointLightLocations = std::make_shared(); - _spotLightLocations = std::make_shared(); - - loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations); - loadLightProgram(deferred_light_vert, directional_light_shadow_map_frag, false, _directionalLightShadowMap, - _directionalLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap, - _directionalLightCascadedShadowMapLocations); - - loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap, - _directionalAmbientSphereLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap, - _directionalAmbientSphereLightCascadedShadowMapLocations); - - loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap, - _directionalSkyboxLightShadowMapLocations); - loadLightProgram(deferred_light_vert, directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap, - _directionalSkyboxLightCascadedShadowMapLocations); - - - loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations); - loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations); - - { - //auto VSFS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - //auto PSBlit = gpu::StandardShaderLib::getDrawTexturePS(); - auto blitProgram = gpu::StandardShaderLib::getProgram(gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS, gpu::StandardShaderLib::getDrawTexturePS); - gpu::Shader::makeProgram(*blitProgram); - auto blitState = std::make_shared(); - blitState->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - blitState->setColorWriteMask(true, true, true, false); - _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState)); - } - - // Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light - _globalLights.push_back(0); - _allocatedLights.push_back(std::make_shared()); - - model::LightPointer lp = _allocatedLights[0]; - - lp->setDirection(-glm::vec3(1.0f, 1.0f, 1.0f)); - lp->setColor(glm::vec3(1.0f)); - lp->setIntensity(1.0f); - lp->setType(model::Light::SUN); - lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET)); -} - - - -gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, - bool emmisive, bool depthBias) { - SimpleProgramKey config{textured, culled, emmisive, depthBias}; - gpu::PipelinePointer pipeline = getPipeline(config); - batch.setPipeline(pipeline); - - gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; - int glowIntensity = program->getUniforms().findLocation("glowIntensity"); - batch._glUniform1f(glowIntensity, 1.0f); - - if (!config.isTextured()) { - // If it is not textured, bind white texture and keep using textured pipeline - batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); - } - - batch.setResourceTexture(NORMAL_FITTING_MAP_SLOT, DependencyManager::get()->getNormalFittingTexture()); - return pipeline; -} - -uint32_t toCompactColor(const glm::vec4& color) { - uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | - ((int(color.y * 255.0f) & 0xFF) << 8) | - ((int(color.z * 255.0f) & 0xFF) << 16) | - ((int(color.w * 255.0f) & 0xFF) << 24); - return compactColor; -} - -static const size_t INSTANCE_TRANSFORM_BUFFER = 0; -static const size_t INSTANCE_COLOR_BUFFER = 1; - -template -void renderInstances(const std::string& name, gpu::Batch& batch, const Transform& transform, const glm::vec4& color, F f) { - { - gpu::BufferPointer instanceTransformBuffer = batch.getNamedBuffer(name, INSTANCE_TRANSFORM_BUFFER); - glm::mat4 glmTransform; - instanceTransformBuffer->append(transform.getMatrix(glmTransform)); - - gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(name, INSTANCE_COLOR_BUFFER); - auto compactColor = toCompactColor(color); - instanceColorBuffer->append(compactColor); - } - - auto that = DependencyManager::get(); - batch.setupNamedCalls(name, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - auto pipeline = that->bindSimpleProgram(batch); - auto location = pipeline->getProgram()->getUniforms().findLocation("Instanced"); - - batch._glUniform1i(location, 1); - f(batch, data); - batch._glUniform1i(location, 0); - }); -} - -void DeferredLightingEffect::renderSolidSphereInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { - static const std::string INSTANCE_NAME = __FUNCTION__; - renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - DependencyManager::get()->renderShapeInstances(batch, GeometryCache::Sphere, data._count, - data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); - }); -} - -void DeferredLightingEffect::renderWireSphereInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { - static const std::string INSTANCE_NAME = __FUNCTION__; - renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - DependencyManager::get()->renderWireShapeInstances(batch, GeometryCache::Sphere, data._count, - data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); - }); -} - -// Enable this in a debug build to cause 'box' entities to iterate through all the -// available shape types, both solid and wireframes -//#define DEBUG_SHAPES - -void DeferredLightingEffect::renderSolidCubeInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { - static const std::string INSTANCE_NAME = __FUNCTION__; - -#ifdef DEBUG_SHAPES - static auto startTime = usecTimestampNow(); - renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - - auto usecs = usecTimestampNow(); - usecs -= startTime; - auto msecs = usecs / USECS_PER_MSEC; - float seconds = msecs; - seconds /= MSECS_PER_SECOND; - float fractionalSeconds = seconds - floor(seconds); - int shapeIndex = (int)seconds; - - // Every second we flip to the next shape. - static const int SHAPE_COUNT = 5; - GeometryCache::Shape shapes[SHAPE_COUNT] = { - GeometryCache::Cube, - GeometryCache::Tetrahedron, - GeometryCache::Sphere, - GeometryCache::Icosahedron, - GeometryCache::Line, - }; - - shapeIndex %= SHAPE_COUNT; - GeometryCache::Shape shape = shapes[shapeIndex]; - - // For the first half second for a given shape, show the wireframe, for the second half, show the solid. - if (fractionalSeconds > 0.5f) { - DependencyManager::get()->renderShapeInstances(batch, shape, data._count, - data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); - } else { - DependencyManager::get()->renderWireShapeInstances(batch, shape, data._count, - data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); - } - }); -#else - renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - DependencyManager::get()->renderCubeInstances(batch, data._count, - data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); - }); -#endif -} - -void DeferredLightingEffect::renderWireCubeInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { - static const std::string INSTANCE_NAME = __FUNCTION__; - renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { - DependencyManager::get()->renderWireCubeInstances(batch, data._count, - data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); - }); -} - -void DeferredLightingEffect::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, - const glm::vec4& color) { - bindSimpleProgram(batch); - DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, color); -} - -void DeferredLightingEffect::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, - const glm::vec4& color1, const glm::vec4& color2) { - bindSimpleProgram(batch); - DependencyManager::get()->renderLine(batch, p1, p2, color1, color2); -} - -void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color, - float intensity) { - addSpotLight(position, radius, color, intensity); -} - -void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color, - float intensity, const glm::quat& orientation, float exponent, float cutoff) { - - unsigned int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size(); - if (lightID >= _allocatedLights.size()) { - _allocatedLights.push_back(std::make_shared()); - } - model::LightPointer lp = _allocatedLights[lightID]; - - lp->setPosition(position); - lp->setMaximumRadius(radius); - lp->setColor(color); - lp->setIntensity(intensity); - //lp->setShowContour(quadraticAttenuation); - - if (exponent == 0.0f && cutoff == PI) { - lp->setType(model::Light::POINT); - _pointLights.push_back(lightID); - - } else { - lp->setOrientation(orientation); - lp->setSpotAngle(cutoff); - lp->setSpotExponent(exponent); - lp->setType(model::Light::SPOT); - _spotLights.push_back(lightID); - } -} - -void DeferredLightingEffect::prepare(RenderArgs* args) { - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setStateScissorRect(args->_viewport); - - auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); - - batch.setFramebuffer(primaryFbo); - // clear the normal and specular buffers - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); - const float MAX_SPECULAR_EXPONENT = 128.0f; - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT), true); - }); -} - -gpu::FramebufferPointer _copyFBO; - -void DeferredLightingEffect::render(RenderArgs* args) { - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - - // Allocate the parameters buffer used by all the deferred shaders - if (!_deferredTransformBuffer[0]._buffer) { - DeferredTransform parameters; - _deferredTransformBuffer[0] = gpu::BufferView(std::make_shared(sizeof(DeferredTransform), (const gpu::Byte*) ¶meters)); - _deferredTransformBuffer[1] = gpu::BufferView(std::make_shared(sizeof(DeferredTransform), (const gpu::Byte*) ¶meters)); - } - - // Framebuffer copy operations cannot function as multipass stereo operations. - batch.enableStereo(false); - - // perform deferred lighting, rendering to free fbo - auto framebufferCache = DependencyManager::get(); - - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - - // binding the first framebuffer - _copyFBO = framebufferCache->getFramebuffer(); - batch.setFramebuffer(_copyFBO); - - // Clearing it - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); - - // BInd the G-Buffer surfaces - batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); - batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); - batch.setResourceTexture(2, framebufferCache->getPrimarySpecularTexture()); - batch.setResourceTexture(3, framebufferCache->getPrimaryDepthTexture()); - - // THe main viewport is assumed to be the mono viewport (or the 2 stereo faces side by side within that viewport) - auto monoViewport = args->_viewport; - float sMin = args->_viewport.x / (float)framebufferSize.width(); - float sWidth = args->_viewport.z / (float)framebufferSize.width(); - float tMin = args->_viewport.y / (float)framebufferSize.height(); - float tHeight = args->_viewport.w / (float)framebufferSize.height(); - - // The view frustum is the mono frustum base - auto viewFrustum = args->_viewFrustum; - - // Eval the mono projection - mat4 monoProjMat; - viewFrustum->evalProjectionMatrix(monoProjMat); - - // The mono view transform - Transform monoViewTransform; - viewFrustum->evalViewTransform(monoViewTransform); - - // THe mono view matrix coming from the mono view transform - glm::mat4 monoViewMat; - monoViewTransform.getMatrix(monoViewMat); - - // Running in stero ? - bool isStereo = args->_context->isStereo(); - int numPasses = 1; - - mat4 projMats[2]; - Transform viewTransforms[2]; - ivec4 viewports[2]; - vec4 clipQuad[2]; - vec2 screenBottomLeftCorners[2]; - vec2 screenTopRightCorners[2]; - vec4 fetchTexcoordRects[2]; - - DeferredTransform deferredTransforms[2]; - auto geometryCache = DependencyManager::get(); - - if (isStereo) { - numPasses = 2; - - mat4 eyeViews[2]; - args->_context->getStereoProjections(projMats); - args->_context->getStereoViews(eyeViews); - - float halfWidth = 0.5f * sWidth; - - for (int i = 0; i < numPasses; i++) { - // In stereo, the 2 sides are layout side by side in the mono viewport and their width is half - int sideWidth = monoViewport.z >> 1; - viewports[i] = ivec4(monoViewport.x + (i * sideWidth), monoViewport.y, sideWidth, monoViewport.w); - - deferredTransforms[i].projection = projMats[i]; - - auto sideViewMat = monoViewMat * glm::inverse(eyeViews[i]); - viewTransforms[i].evalFromRawMatrix(sideViewMat); - deferredTransforms[i].viewInverse = sideViewMat; - - deferredTransforms[i].stereoSide = (i == 0 ? -1.0f : 1.0f); - - clipQuad[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight); - screenBottomLeftCorners[i] = glm::vec2(-1.0f + i * 1.0f, -1.0f); - screenTopRightCorners[i] = glm::vec2(i * 1.0f, 1.0f); - - fetchTexcoordRects[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight); - } - } else { - - viewports[0] = monoViewport; - projMats[0] = monoProjMat; - - deferredTransforms[0].projection = monoProjMat; - - deferredTransforms[0].viewInverse = monoViewMat; - viewTransforms[0] = monoViewTransform; - - deferredTransforms[0].stereoSide = 0.0f; - - clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight); - screenBottomLeftCorners[0] = glm::vec2(-1.0f, -1.0f); - screenTopRightCorners[0] = glm::vec2(1.0f, 1.0f); - - fetchTexcoordRects[0] = glm::vec4(sMin, tMin, sWidth, tHeight); - } - - auto eyePoint = viewFrustum->getPosition(); - float nearRadius = glm::distance(eyePoint, viewFrustum->getNearTopLeft()); - - - for (int side = 0; side < numPasses; side++) { - // Render in this side's viewport - batch.setViewportTransform(viewports[side]); - batch.setStateScissorRect(viewports[side]); - - // Sync and Bind the correct DeferredTransform ubo - _deferredTransformBuffer[side]._buffer->setSubData(0, sizeof(DeferredTransform), (const gpu::Byte*) &deferredTransforms[side]); - batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, _deferredTransformBuffer[side]); - - glm::vec2 topLeft(-1.0f, -1.0f); - glm::vec2 bottomRight(1.0f, 1.0f); - glm::vec2 texCoordTopLeft(clipQuad[side].x, clipQuad[side].y); - glm::vec2 texCoordBottomRight(clipQuad[side].x + clipQuad[side].z, clipQuad[side].y + clipQuad[side].w); - - // First Global directional light and ambient pass - { - bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap()); - - auto& program = _directionalLight; - LightLocationsPtr locations = _directionalLightLocations; - - // TODO: At some point bring back the shadows... - // Setup the global directional pass pipeline - { - if (useSkyboxCubemap) { - program = _directionalSkyboxLight; - locations = _directionalSkyboxLightLocations; - } else if (_ambientLightMode > -1) { - program = _directionalAmbientSphereLight; - locations = _directionalAmbientSphereLightLocations; - } - batch.setPipeline(program); - } - - { // Setup the global lighting - auto globalLight = _allocatedLights[_globalLights.front()]; - - if (locations->ambientSphere >= 0) { - gpu::SphericalHarmonics sh = globalLight->getAmbientSphere(); - if (useSkyboxCubemap && _skybox->getCubemap()->getIrradiance()) { - sh = (*_skybox->getCubemap()->getIrradiance()); - } - for (int i =0; i ambientSphere + i, 1, (const float*) (&sh) + i * 4); - } - } - - if (useSkyboxCubemap) { - batch.setResourceTexture(5, _skybox->getCubemap()); - } - - if (locations->lightBufferUnit >= 0) { - batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer()); - } - - if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) { - batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer()); - } - } - - { - batch.setModelTransform(Transform()); - batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); - - glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); - geometryCache->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); - } - - if (useSkyboxCubemap) { - batch.setResourceTexture(5, nullptr); - } - } - - auto texcoordMat = glm::mat4(); - /* texcoordMat[0] = glm::vec4(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f); - texcoordMat[1] = glm::vec4(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f); - */ texcoordMat[0] = glm::vec4(fetchTexcoordRects[side].z / 2.0f, 0.0f, 0.0f, fetchTexcoordRects[side].x + fetchTexcoordRects[side].z / 2.0f); - texcoordMat[1] = glm::vec4(0.0f, fetchTexcoordRects[side].w / 2.0f, 0.0f, fetchTexcoordRects[side].y + fetchTexcoordRects[side].w / 2.0f); - texcoordMat[2] = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); - texcoordMat[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); - - // enlarge the scales slightly to account for tesselation - const float SCALE_EXPANSION = 0.05f; - - - batch.setProjectionTransform(projMats[side]); - batch.setViewTransform(viewTransforms[side]); - - // Splat Point lights - if (!_pointLights.empty()) { - batch.setPipeline(_pointLight); - - batch._glUniformMatrix4fv(_pointLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); - - for (auto lightID : _pointLights) { - auto& light = _allocatedLights[lightID]; - // IN DEBUG: light->setShowContour(true); - batch.setUniformBuffer(_pointLightLocations->lightBufferUnit, light->getSchemaBuffer()); - - float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); - // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, - // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working... - if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) { - Transform model; - model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); - batch.setModelTransform(model); - batch.setViewTransform(Transform()); - batch.setProjectionTransform(glm::mat4()); - - glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); - - batch.setProjectionTransform(projMats[side]); - batch.setViewTransform(viewTransforms[side]); - } else { - Transform model; - model.setTranslation(glm::vec3(light->getPosition().x, light->getPosition().y, light->getPosition().z)); - batch.setModelTransform(model.postScale(expandedRadius)); - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - geometryCache->renderSphere(batch); - } - } - } - - // Splat spot lights - if (!_spotLights.empty()) { - batch.setPipeline(_spotLight); - - batch._glUniformMatrix4fv(_spotLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); - - for (auto lightID : _spotLights) { - auto light = _allocatedLights[lightID]; - // IN DEBUG: light->setShowContour(true); - batch.setUniformBuffer(_spotLightLocations->lightBufferUnit, light->getSchemaBuffer()); - - auto eyeLightPos = eyePoint - light->getPosition(); - auto eyeHalfPlaneDistance = glm::dot(eyeLightPos, light->getDirection()); - - const float TANGENT_LENGTH_SCALE = 0.666f; - glm::vec4 coneParam(light->getSpotAngleCosSin(), TANGENT_LENGTH_SCALE * tanf(0.5f * light->getSpotAngle()), 1.0f); - - float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); - // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, - // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working... - if ((eyeHalfPlaneDistance > -nearRadius) && - (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) { - coneParam.w = 0.0f; - batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam)); - - Transform model; - model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); - batch.setModelTransform(model); - batch.setViewTransform(Transform()); - batch.setProjectionTransform(glm::mat4()); - - glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); - - batch.setProjectionTransform( projMats[side]); - batch.setViewTransform(viewTransforms[side]); - } else { - coneParam.w = 1.0f; - batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam)); - - Transform model; - model.setTranslation(light->getPosition()); - model.postRotate(light->getOrientation()); - model.postScale(glm::vec3(expandedRadius, expandedRadius, expandedRadius)); - - batch.setModelTransform(model); - auto mesh = getSpotLightMesh(); - - batch.setIndexBuffer(mesh->getIndexBuffer()); - batch.setInputBuffer(0, mesh->getVertexBuffer()); - batch.setInputFormat(mesh->getVertexFormat()); - - auto& part = mesh->getPartBuffer().get(); - - batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex); - } - } - } - } - - // Probably not necessary in the long run because the gpu layer would unbound this texture if used as render target - batch.setResourceTexture(0, nullptr); - batch.setResourceTexture(1, nullptr); - batch.setResourceTexture(2, nullptr); - batch.setResourceTexture(3, nullptr); - batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, nullptr); - }); - - // End of the Lighting pass - if (!_pointLights.empty()) { - _pointLights.clear(); - } - if (!_spotLights.empty()) { - _spotLights.clear(); - } -} - - -void DeferredLightingEffect::copyBack(RenderArgs* args) { - auto framebufferCache = DependencyManager::get(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - batch.enableStereo(false); - QSize framebufferSize = framebufferCache->getFrameBufferSize(); - - // TODO why doesn't this blit work? It only seems to affect a small area below the rear view mirror. - // auto destFbo = framebufferCache->getPrimaryFramebuffer(); - auto destFbo = framebufferCache->getPrimaryFramebufferDepthColor(); - // gpu::Vec4i vp = args->_viewport; - // batch.blit(_copyFBO, vp, framebufferCache->getPrimaryFramebuffer(), vp); - batch.setFramebuffer(destFbo); - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(glm::mat4()); - batch.setViewTransform(Transform()); - { - float sMin = args->_viewport.x / (float)framebufferSize.width(); - float sWidth = args->_viewport.z / (float)framebufferSize.width(); - float tMin = args->_viewport.y / (float)framebufferSize.height(); - float tHeight = args->_viewport.w / (float)framebufferSize.height(); - Transform model; - batch.setPipeline(_blitLightBuffer); - model.setTranslation(glm::vec3(sMin, tMin, 0.0)); - model.setScale(glm::vec3(sWidth, tHeight, 1.0)); - batch.setModelTransform(model); - } - - batch.setResourceTexture(0, _copyFBO->getRenderBuffer(0)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - - args->_context->render(batch); - }); - framebufferCache->releaseFramebuffer(_copyFBO); -} - -void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferUnit) { - auto globalLight = _allocatedLights[_globalLights.front()]; - args->_batch->setUniformBuffer(lightBufferUnit, globalLight->getSchemaBuffer()); -} - -static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) { - auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource))); - auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource))); - - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), 0)); - slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1)); - slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); - slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); - slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); - slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); - const int LIGHT_GPU_SLOT = 3; - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); - const int ATMOSPHERE_GPU_SLOT = 4; - slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT)); - - slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), DeferredLightingEffect::DEFERRED_TRANSFORM_BUFFER_SLOT)); - - gpu::Shader::makeProgram(*program, slotBindings); - - locations->shadowDistances = program->getUniforms().findLocation("shadowDistances"); - locations->shadowScale = program->getUniforms().findLocation("shadowScale"); - - locations->radius = program->getUniforms().findLocation("radius"); - locations->ambientSphere = program->getUniforms().findLocation("ambientSphere.L00"); - - locations->texcoordMat = program->getUniforms().findLocation("texcoordMat"); - locations->coneParam = program->getUniforms().findLocation("coneParam"); - - locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); - locations->atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit"); - locations->deferredTransformBuffer = program->getBuffers().findLocation("deferredTransformBuffer"); - - auto state = std::make_shared(); - if (lightVolume) { - state->setCullMode(gpu::State::CULL_BACK); - - // No need for z test since the depth buffer is not bound state->setDepthTest(true, false, gpu::LESS_EQUAL); - // TODO: We should bind the true depth buffer both as RT and texture for the depth test - // TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases - state->setDepthClampEnable(true); - - // additive blending - state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - } else { - state->setCullMode(gpu::State::CULL_BACK); - } - pipeline.reset(gpu::Pipeline::create(program, state)); - -} - -void DeferredLightingEffect::setAmbientLightMode(int preset) { - if ((preset >= 0) && (preset < gpu::SphericalHarmonics::NUM_PRESET)) { - _ambientLightMode = preset; - auto light = _allocatedLights.front(); - light->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(preset % gpu::SphericalHarmonics::NUM_PRESET)); - } else { - // force to preset 0 - setAmbientLightMode(0); - } -} - -void DeferredLightingEffect::setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity, float ambientIntensity) { - auto light = _allocatedLights.front(); - light->setDirection(direction); - light->setColor(diffuse); - light->setIntensity(intensity); - light->setAmbientIntensity(ambientIntensity); -} - -void DeferredLightingEffect::setGlobalSkybox(const model::SkyboxPointer& skybox) { - _skybox = skybox; -} - -model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { - if (!_spotLightMesh) { - _spotLightMesh = std::make_shared(); - - int slices = 32; - int rings = 3; - int vertices = 2 + rings * slices; - int originVertex = vertices - 2; - int capVertex = vertices - 1; - int verticesSize = vertices * 3 * sizeof(float); - int indices = 3 * slices * (1 + 1 + 2 * (rings -1)); - int ringFloatOffset = slices * 3; - - - float* vertexData = new float[verticesSize]; - float* vertexRing0 = vertexData; - float* vertexRing1 = vertexRing0 + ringFloatOffset; - float* vertexRing2 = vertexRing1 + ringFloatOffset; - - for (int i = 0; i < slices; i++) { - float theta = TWO_PI * i / slices; - auto cosin = glm::vec2(cosf(theta), sinf(theta)); - - *(vertexRing0++) = cosin.x; - *(vertexRing0++) = cosin.y; - *(vertexRing0++) = 0.0f; - - *(vertexRing1++) = cosin.x; - *(vertexRing1++) = cosin.y; - *(vertexRing1++) = 0.33f; - - *(vertexRing2++) = cosin.x; - *(vertexRing2++) = cosin.y; - *(vertexRing2++) = 0.66f; - } - - *(vertexRing2++) = 0.0f; - *(vertexRing2++) = 0.0f; - *(vertexRing2++) = -1.0f; - - *(vertexRing2++) = 0.0f; - *(vertexRing2++) = 0.0f; - *(vertexRing2++) = 1.0f; - - _spotLightMesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(verticesSize, (gpu::Byte*) vertexData), gpu::Element::VEC3F_XYZ)); - delete[] vertexData; - - gpu::uint16* indexData = new gpu::uint16[indices]; - gpu::uint16* index = indexData; - for (int i = 0; i < slices; i++) { - *(index++) = originVertex; - - int s0 = i; - int s1 = ((i + 1) % slices); - *(index++) = s0; - *(index++) = s1; - - int s2 = s0 + slices; - int s3 = s1 + slices; - *(index++) = s1; - *(index++) = s0; - *(index++) = s2; - - *(index++) = s1; - *(index++) = s2; - *(index++) = s3; - - int s4 = s2 + slices; - int s5 = s3 + slices; - *(index++) = s3; - *(index++) = s2; - *(index++) = s4; - - *(index++) = s3; - *(index++) = s4; - *(index++) = s5; - - - *(index++) = s5; - *(index++) = s4; - *(index++) = capVertex; - } - - _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); - delete[] indexData; - - model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES); - //DEBUG: model::Mesh::Part part(0, indices, 0, model::Mesh::LINE_STRIP); - - _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL)); - - _spotLightMesh->getVertexStream(); - } - return _spotLightMesh; -} - +// +// DeferredLightingEffect.cpp +// interface/src/renderer +// +// Created by Andrzej Kapolka on 9/11/14. +// 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 "DeferredLightingEffect.h" + +#include +#include +#include + +#include +#include +#include + +#include "AbstractViewStateInterface.h" +#include "GeometryCache.h" +#include "TextureCache.h" +#include "FramebufferCache.h" + + +#include "simple_vert.h" +#include "simple_textured_frag.h" +#include "simple_textured_emisive_frag.h" + +#include "deferred_light_vert.h" +#include "deferred_light_limited_vert.h" +#include "deferred_light_spot_vert.h" + +#include "directional_light_frag.h" +#include "directional_light_shadow_map_frag.h" +#include "directional_light_cascaded_shadow_map_frag.h" + +#include "directional_ambient_light_frag.h" +#include "directional_ambient_light_shadow_map_frag.h" +#include "directional_ambient_light_cascaded_shadow_map_frag.h" + +#include "directional_skybox_light_frag.h" +#include "directional_skybox_light_shadow_map_frag.h" +#include "directional_skybox_light_cascaded_shadow_map_frag.h" + +#include "point_light_frag.h" +#include "spot_light_frag.h" + +static const std::string glowIntensityShaderHandle = "glowIntensity"; + +struct LightLocations { + int shadowDistances; + int shadowScale; + int radius; + int ambientSphere; + int lightBufferUnit; + int atmosphereBufferUnit; + int texcoordMat; + int coneParam; + int deferredTransformBuffer; +}; + +static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& program, LightLocationsPtr& locations); + + +gpu::PipelinePointer DeferredLightingEffect::getPipeline(SimpleProgramKey config) { + auto it = _simplePrograms.find(config); + if (it != _simplePrograms.end()) { + return it.value(); + } + + auto state = std::make_shared(); + if (config.isCulled()) { + state->setCullMode(gpu::State::CULL_BACK); + } else { + state->setCullMode(gpu::State::CULL_NONE); + } + state->setDepthTest(true, true, gpu::LESS_EQUAL); + if (config.hasDepthBias()) { + state->setDepthBias(1.0f); + state->setDepthBiasSlopeScale(1.0f); + } + state->setBlendFunction(false, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; + gpu::PipelinePointer pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + _simplePrograms.insert(config, pipeline); + return pipeline; +} + +void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { + auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(simple_vert))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_frag))); + auto PSEmissive = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(simple_textured_emisive_frag))); + + _simpleShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + _emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive)); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); + gpu::Shader::makeProgram(*_simpleShader, slotBindings); + gpu::Shader::makeProgram(*_emissiveShader, slotBindings); + + _viewState = viewState; + _directionalLightLocations = std::make_shared(); + _directionalLightShadowMapLocations = std::make_shared(); + _directionalLightCascadedShadowMapLocations = std::make_shared(); + _directionalAmbientSphereLightLocations = std::make_shared(); + _directionalAmbientSphereLightShadowMapLocations = std::make_shared(); + _directionalAmbientSphereLightCascadedShadowMapLocations = std::make_shared(); + _directionalSkyboxLightLocations = std::make_shared(); + _directionalSkyboxLightShadowMapLocations = std::make_shared(); + _directionalSkyboxLightCascadedShadowMapLocations = std::make_shared(); + _pointLightLocations = std::make_shared(); + _spotLightLocations = std::make_shared(); + + loadLightProgram(deferred_light_vert, directional_light_frag, false, _directionalLight, _directionalLightLocations); + loadLightProgram(deferred_light_vert, directional_light_shadow_map_frag, false, _directionalLightShadowMap, + _directionalLightShadowMapLocations); + loadLightProgram(deferred_light_vert, directional_light_cascaded_shadow_map_frag, false, _directionalLightCascadedShadowMap, + _directionalLightCascadedShadowMapLocations); + + loadLightProgram(deferred_light_vert, directional_ambient_light_frag, false, _directionalAmbientSphereLight, _directionalAmbientSphereLightLocations); + loadLightProgram(deferred_light_vert, directional_ambient_light_shadow_map_frag, false, _directionalAmbientSphereLightShadowMap, + _directionalAmbientSphereLightShadowMapLocations); + loadLightProgram(deferred_light_vert, directional_ambient_light_cascaded_shadow_map_frag, false, _directionalAmbientSphereLightCascadedShadowMap, + _directionalAmbientSphereLightCascadedShadowMapLocations); + + loadLightProgram(deferred_light_vert, directional_skybox_light_frag, false, _directionalSkyboxLight, _directionalSkyboxLightLocations); + loadLightProgram(deferred_light_vert, directional_skybox_light_shadow_map_frag, false, _directionalSkyboxLightShadowMap, + _directionalSkyboxLightShadowMapLocations); + loadLightProgram(deferred_light_vert, directional_skybox_light_cascaded_shadow_map_frag, false, _directionalSkyboxLightCascadedShadowMap, + _directionalSkyboxLightCascadedShadowMapLocations); + + + loadLightProgram(deferred_light_limited_vert, point_light_frag, true, _pointLight, _pointLightLocations); + loadLightProgram(deferred_light_spot_vert, spot_light_frag, true, _spotLight, _spotLightLocations); + + { + //auto VSFS = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + //auto PSBlit = gpu::StandardShaderLib::getDrawTexturePS(); + auto blitProgram = gpu::StandardShaderLib::getProgram(gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS, gpu::StandardShaderLib::getDrawTexturePS); + gpu::Shader::makeProgram(*blitProgram); + auto blitState = std::make_shared(); + blitState->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + blitState->setColorWriteMask(true, true, true, false); + _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState)); + } + + // Allocate a global light representing the Global Directional light casting shadow (the sun) and the ambient light + _globalLights.push_back(0); + _allocatedLights.push_back(std::make_shared()); + + model::LightPointer lp = _allocatedLights[0]; + + lp->setDirection(-glm::vec3(1.0f, 1.0f, 1.0f)); + lp->setColor(glm::vec3(1.0f)); + lp->setIntensity(1.0f); + lp->setType(model::Light::SUN); + lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(_ambientLightMode % gpu::SphericalHarmonics::NUM_PRESET)); +} + + + +gpu::PipelinePointer DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, + bool emmisive, bool depthBias) { + SimpleProgramKey config{textured, culled, emmisive, depthBias}; + gpu::PipelinePointer pipeline = getPipeline(config); + batch.setPipeline(pipeline); + + gpu::ShaderPointer program = (config.isEmissive()) ? _emissiveShader : _simpleShader; + int glowIntensity = program->getUniforms().findLocation("glowIntensity"); + batch._glUniform1f(glowIntensity, 1.0f); + + if (!config.isTextured()) { + // If it is not textured, bind white texture and keep using textured pipeline + batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); + } + + batch.setResourceTexture(NORMAL_FITTING_MAP_SLOT, DependencyManager::get()->getNormalFittingTexture()); + return pipeline; +} + +uint32_t toCompactColor(const glm::vec4& color) { + uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | + ((int(color.y * 255.0f) & 0xFF) << 8) | + ((int(color.z * 255.0f) & 0xFF) << 16) | + ((int(color.w * 255.0f) & 0xFF) << 24); + return compactColor; +} + +static const size_t INSTANCE_TRANSFORM_BUFFER = 0; +static const size_t INSTANCE_COLOR_BUFFER = 1; + +template +void renderInstances(const std::string& name, gpu::Batch& batch, const Transform& transform, const glm::vec4& color, F f) { + { + gpu::BufferPointer instanceTransformBuffer = batch.getNamedBuffer(name, INSTANCE_TRANSFORM_BUFFER); + glm::mat4 glmTransform; + instanceTransformBuffer->append(transform.getMatrix(glmTransform)); + + gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(name, INSTANCE_COLOR_BUFFER); + auto compactColor = toCompactColor(color); + instanceColorBuffer->append(compactColor); + } + + auto that = DependencyManager::get(); + batch.setupNamedCalls(name, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + auto pipeline = that->bindSimpleProgram(batch); + auto location = pipeline->getProgram()->getUniforms().findLocation("Instanced"); + + batch._glUniform1i(location, 1); + f(batch, data); + batch._glUniform1i(location, 0); + }); +} + +void DeferredLightingEffect::renderSolidSphereInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { + static const std::string INSTANCE_NAME = __FUNCTION__; + renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + DependencyManager::get()->renderShapeInstances(batch, GeometryCache::Sphere, data._count, + data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); + }); +} + +void DeferredLightingEffect::renderWireSphereInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { + static const std::string INSTANCE_NAME = __FUNCTION__; + renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + DependencyManager::get()->renderWireShapeInstances(batch, GeometryCache::Sphere, data._count, + data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); + }); +} + +// Enable this in a debug build to cause 'box' entities to iterate through all the +// available shape types, both solid and wireframes +//#define DEBUG_SHAPES + +void DeferredLightingEffect::renderSolidCubeInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { + static const std::string INSTANCE_NAME = __FUNCTION__; + +#ifdef DEBUG_SHAPES + static auto startTime = usecTimestampNow(); + renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + + auto usecs = usecTimestampNow(); + usecs -= startTime; + auto msecs = usecs / USECS_PER_MSEC; + float seconds = msecs; + seconds /= MSECS_PER_SECOND; + float fractionalSeconds = seconds - floor(seconds); + int shapeIndex = (int)seconds; + + // Every second we flip to the next shape. + static const int SHAPE_COUNT = 5; + GeometryCache::Shape shapes[SHAPE_COUNT] = { + GeometryCache::Cube, + GeometryCache::Tetrahedron, + GeometryCache::Sphere, + GeometryCache::Icosahedron, + GeometryCache::Line, + }; + + shapeIndex %= SHAPE_COUNT; + GeometryCache::Shape shape = shapes[shapeIndex]; + + // For the first half second for a given shape, show the wireframe, for the second half, show the solid. + if (fractionalSeconds > 0.5f) { + DependencyManager::get()->renderShapeInstances(batch, shape, data._count, + data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); + } else { + DependencyManager::get()->renderWireShapeInstances(batch, shape, data._count, + data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); + } + }); +#else + renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + DependencyManager::get()->renderCubeInstances(batch, data._count, + data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); + }); +#endif +} + +void DeferredLightingEffect::renderWireCubeInstance(gpu::Batch& batch, const Transform& transform, const glm::vec4& color) { + static const std::string INSTANCE_NAME = __FUNCTION__; + renderInstances(INSTANCE_NAME, batch, transform, color, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { + DependencyManager::get()->renderWireCubeInstances(batch, data._count, + data._buffers[INSTANCE_TRANSFORM_BUFFER], data._buffers[INSTANCE_COLOR_BUFFER]); + }); +} + +void DeferredLightingEffect::renderQuad(gpu::Batch& batch, const glm::vec3& minCorner, const glm::vec3& maxCorner, + const glm::vec4& color) { + bindSimpleProgram(batch); + DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, color); +} + +void DeferredLightingEffect::renderLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, + const glm::vec4& color1, const glm::vec4& color2) { + bindSimpleProgram(batch); + DependencyManager::get()->renderLine(batch, p1, p2, color1, color2); +} + +void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color, + float intensity) { + addSpotLight(position, radius, color, intensity); +} + +void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color, + float intensity, const glm::quat& orientation, float exponent, float cutoff) { + + unsigned int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size(); + if (lightID >= _allocatedLights.size()) { + _allocatedLights.push_back(std::make_shared()); + } + model::LightPointer lp = _allocatedLights[lightID]; + + lp->setPosition(position); + lp->setMaximumRadius(radius); + lp->setColor(color); + lp->setIntensity(intensity); + //lp->setShowContour(quadraticAttenuation); + + if (exponent == 0.0f && cutoff == PI) { + lp->setType(model::Light::POINT); + _pointLights.push_back(lightID); + + } else { + lp->setOrientation(orientation); + lp->setSpotAngle(cutoff); + lp->setSpotExponent(exponent); + lp->setType(model::Light::SPOT); + _spotLights.push_back(lightID); + } +} + +void DeferredLightingEffect::prepare(RenderArgs* args) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setStateScissorRect(args->_viewport); + + auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); + + batch.setFramebuffer(primaryFbo); + // clear the normal and specular buffers + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); + const float MAX_SPECULAR_EXPONENT = 128.0f; + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT), true); + }); +} + +gpu::FramebufferPointer _copyFBO; + +void DeferredLightingEffect::render(RenderArgs* args) { + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + + // Allocate the parameters buffer used by all the deferred shaders + if (!_deferredTransformBuffer[0]._buffer) { + DeferredTransform parameters; + _deferredTransformBuffer[0] = gpu::BufferView(std::make_shared(sizeof(DeferredTransform), (const gpu::Byte*) ¶meters)); + _deferredTransformBuffer[1] = gpu::BufferView(std::make_shared(sizeof(DeferredTransform), (const gpu::Byte*) ¶meters)); + } + + // Framebuffer copy operations cannot function as multipass stereo operations. + batch.enableStereo(false); + + // perform deferred lighting, rendering to free fbo + auto framebufferCache = DependencyManager::get(); + + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + + // binding the first framebuffer + _copyFBO = framebufferCache->getFramebuffer(); + batch.setFramebuffer(_copyFBO); + + // Clearing it + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + batch.clearColorFramebuffer(_copyFBO->getBufferMask(), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); + + // BInd the G-Buffer surfaces + batch.setResourceTexture(0, framebufferCache->getPrimaryColorTexture()); + batch.setResourceTexture(1, framebufferCache->getPrimaryNormalTexture()); + batch.setResourceTexture(2, framebufferCache->getPrimarySpecularTexture()); + batch.setResourceTexture(3, framebufferCache->getPrimaryDepthTexture()); + + // THe main viewport is assumed to be the mono viewport (or the 2 stereo faces side by side within that viewport) + auto monoViewport = args->_viewport; + float sMin = args->_viewport.x / (float)framebufferSize.width(); + float sWidth = args->_viewport.z / (float)framebufferSize.width(); + float tMin = args->_viewport.y / (float)framebufferSize.height(); + float tHeight = args->_viewport.w / (float)framebufferSize.height(); + + // The view frustum is the mono frustum base + auto viewFrustum = args->_viewFrustum; + + // Eval the mono projection + mat4 monoProjMat; + viewFrustum->evalProjectionMatrix(monoProjMat); + + // The mono view transform + Transform monoViewTransform; + viewFrustum->evalViewTransform(monoViewTransform); + + // THe mono view matrix coming from the mono view transform + glm::mat4 monoViewMat; + monoViewTransform.getMatrix(monoViewMat); + + // Running in stero ? + bool isStereo = args->_context->isStereo(); + int numPasses = 1; + + mat4 projMats[2]; + Transform viewTransforms[2]; + ivec4 viewports[2]; + vec4 clipQuad[2]; + vec2 screenBottomLeftCorners[2]; + vec2 screenTopRightCorners[2]; + vec4 fetchTexcoordRects[2]; + + DeferredTransform deferredTransforms[2]; + auto geometryCache = DependencyManager::get(); + + if (isStereo) { + numPasses = 2; + + mat4 eyeViews[2]; + args->_context->getStereoProjections(projMats); + args->_context->getStereoViews(eyeViews); + + float halfWidth = 0.5f * sWidth; + + for (int i = 0; i < numPasses; i++) { + // In stereo, the 2 sides are layout side by side in the mono viewport and their width is half + int sideWidth = monoViewport.z >> 1; + viewports[i] = ivec4(monoViewport.x + (i * sideWidth), monoViewport.y, sideWidth, monoViewport.w); + + deferredTransforms[i].projection = projMats[i]; + + auto sideViewMat = monoViewMat * glm::inverse(eyeViews[i]); + viewTransforms[i].evalFromRawMatrix(sideViewMat); + deferredTransforms[i].viewInverse = sideViewMat; + + deferredTransforms[i].stereoSide = (i == 0 ? -1.0f : 1.0f); + + clipQuad[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight); + screenBottomLeftCorners[i] = glm::vec2(-1.0f + i * 1.0f, -1.0f); + screenTopRightCorners[i] = glm::vec2(i * 1.0f, 1.0f); + + fetchTexcoordRects[i] = glm::vec4(sMin + i * halfWidth, tMin, halfWidth, tHeight); + } + } else { + + viewports[0] = monoViewport; + projMats[0] = monoProjMat; + + deferredTransforms[0].projection = monoProjMat; + + deferredTransforms[0].viewInverse = monoViewMat; + viewTransforms[0] = monoViewTransform; + + deferredTransforms[0].stereoSide = 0.0f; + + clipQuad[0] = glm::vec4(sMin, tMin, sWidth, tHeight); + screenBottomLeftCorners[0] = glm::vec2(-1.0f, -1.0f); + screenTopRightCorners[0] = glm::vec2(1.0f, 1.0f); + + fetchTexcoordRects[0] = glm::vec4(sMin, tMin, sWidth, tHeight); + } + + auto eyePoint = viewFrustum->getPosition(); + float nearRadius = glm::distance(eyePoint, viewFrustum->getNearTopLeft()); + + + for (int side = 0; side < numPasses; side++) { + // Render in this side's viewport + batch.setViewportTransform(viewports[side]); + batch.setStateScissorRect(viewports[side]); + + // Sync and Bind the correct DeferredTransform ubo + _deferredTransformBuffer[side]._buffer->setSubData(0, sizeof(DeferredTransform), (const gpu::Byte*) &deferredTransforms[side]); + batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, _deferredTransformBuffer[side]); + + glm::vec2 topLeft(-1.0f, -1.0f); + glm::vec2 bottomRight(1.0f, 1.0f); + glm::vec2 texCoordTopLeft(clipQuad[side].x, clipQuad[side].y); + glm::vec2 texCoordBottomRight(clipQuad[side].x + clipQuad[side].z, clipQuad[side].y + clipQuad[side].w); + + // First Global directional light and ambient pass + { + bool useSkyboxCubemap = (_skybox) && (_skybox->getCubemap()); + + auto& program = _directionalLight; + LightLocationsPtr locations = _directionalLightLocations; + + // TODO: At some point bring back the shadows... + // Setup the global directional pass pipeline + { + if (useSkyboxCubemap) { + program = _directionalSkyboxLight; + locations = _directionalSkyboxLightLocations; + } else if (_ambientLightMode > -1) { + program = _directionalAmbientSphereLight; + locations = _directionalAmbientSphereLightLocations; + } + batch.setPipeline(program); + } + + { // Setup the global lighting + auto globalLight = _allocatedLights[_globalLights.front()]; + + if (locations->ambientSphere >= 0) { + gpu::SphericalHarmonics sh = globalLight->getAmbientSphere(); + if (useSkyboxCubemap && _skybox->getCubemap()->getIrradiance()) { + sh = (*_skybox->getCubemap()->getIrradiance()); + } + for (int i =0; i ambientSphere + i, 1, (const float*) (&sh) + i * 4); + } + } + + if (useSkyboxCubemap) { + batch.setResourceTexture(5, _skybox->getCubemap()); + } + + if (locations->lightBufferUnit >= 0) { + batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer()); + } + + if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) { + batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer()); + } + } + + { + batch.setModelTransform(Transform()); + batch.setProjectionTransform(glm::mat4()); + batch.setViewTransform(Transform()); + + glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); + geometryCache->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); + } + + if (useSkyboxCubemap) { + batch.setResourceTexture(5, nullptr); + } + } + + auto texcoordMat = glm::mat4(); + /* texcoordMat[0] = glm::vec4(sWidth / 2.0f, 0.0f, 0.0f, sMin + sWidth / 2.0f); + texcoordMat[1] = glm::vec4(0.0f, tHeight / 2.0f, 0.0f, tMin + tHeight / 2.0f); + */ texcoordMat[0] = glm::vec4(fetchTexcoordRects[side].z / 2.0f, 0.0f, 0.0f, fetchTexcoordRects[side].x + fetchTexcoordRects[side].z / 2.0f); + texcoordMat[1] = glm::vec4(0.0f, fetchTexcoordRects[side].w / 2.0f, 0.0f, fetchTexcoordRects[side].y + fetchTexcoordRects[side].w / 2.0f); + texcoordMat[2] = glm::vec4(0.0f, 0.0f, 1.0f, 0.0f); + texcoordMat[3] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + + // enlarge the scales slightly to account for tesselation + const float SCALE_EXPANSION = 0.05f; + + + batch.setProjectionTransform(projMats[side]); + batch.setViewTransform(viewTransforms[side]); + + // Splat Point lights + if (!_pointLights.empty()) { + batch.setPipeline(_pointLight); + + batch._glUniformMatrix4fv(_pointLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); + + for (auto lightID : _pointLights) { + auto& light = _allocatedLights[lightID]; + // IN DEBUG: light->setShowContour(true); + batch.setUniformBuffer(_pointLightLocations->lightBufferUnit, light->getSchemaBuffer()); + + float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); + // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, + // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working... + if (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius) { + Transform model; + model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); + batch.setModelTransform(model); + batch.setViewTransform(Transform()); + batch.setProjectionTransform(glm::mat4()); + + glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); + + batch.setProjectionTransform(projMats[side]); + batch.setViewTransform(viewTransforms[side]); + } else { + Transform model; + model.setTranslation(glm::vec3(light->getPosition().x, light->getPosition().y, light->getPosition().z)); + batch.setModelTransform(model.postScale(expandedRadius)); + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + geometryCache->renderSphere(batch); + } + } + } + + // Splat spot lights + if (!_spotLights.empty()) { + batch.setPipeline(_spotLight); + + batch._glUniformMatrix4fv(_spotLightLocations->texcoordMat, 1, false, reinterpret_cast< const float* >(&texcoordMat)); + + for (auto lightID : _spotLights) { + auto light = _allocatedLights[lightID]; + // IN DEBUG: light->setShowContour(true); + batch.setUniformBuffer(_spotLightLocations->lightBufferUnit, light->getSchemaBuffer()); + + auto eyeLightPos = eyePoint - light->getPosition(); + auto eyeHalfPlaneDistance = glm::dot(eyeLightPos, light->getDirection()); + + const float TANGENT_LENGTH_SCALE = 0.666f; + glm::vec4 coneParam(light->getSpotAngleCosSin(), TANGENT_LENGTH_SCALE * tanf(0.5f * light->getSpotAngle()), 1.0f); + + float expandedRadius = light->getMaximumRadius() * (1.0f + SCALE_EXPANSION); + // TODO: We shouldn;t have to do that test and use a different volume geometry for when inside the vlight volume, + // we should be able to draw thre same geometry use DepthClamp but for unknown reason it's s not working... + if ((eyeHalfPlaneDistance > -nearRadius) && + (glm::distance(eyePoint, glm::vec3(light->getPosition())) < expandedRadius + nearRadius)) { + coneParam.w = 0.0f; + batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam)); + + Transform model; + model.setTranslation(glm::vec3(0.0f, 0.0f, -1.0f)); + batch.setModelTransform(model); + batch.setViewTransform(Transform()); + batch.setProjectionTransform(glm::mat4()); + + glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, color); + + batch.setProjectionTransform( projMats[side]); + batch.setViewTransform(viewTransforms[side]); + } else { + coneParam.w = 1.0f; + batch._glUniform4fv(_spotLightLocations->coneParam, 1, reinterpret_cast< const float* >(&coneParam)); + + Transform model; + model.setTranslation(light->getPosition()); + model.postRotate(light->getOrientation()); + model.postScale(glm::vec3(expandedRadius, expandedRadius, expandedRadius)); + + batch.setModelTransform(model); + auto mesh = getSpotLightMesh(); + + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputBuffer(0, mesh->getVertexBuffer()); + batch.setInputFormat(mesh->getVertexFormat()); + + auto& part = mesh->getPartBuffer().get(); + + batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex); + } + } + } + } + + // Probably not necessary in the long run because the gpu layer would unbound this texture if used as render target + batch.setResourceTexture(0, nullptr); + batch.setResourceTexture(1, nullptr); + batch.setResourceTexture(2, nullptr); + batch.setResourceTexture(3, nullptr); + batch.setUniformBuffer(_directionalLightLocations->deferredTransformBuffer, nullptr); + }); + + // End of the Lighting pass + if (!_pointLights.empty()) { + _pointLights.clear(); + } + if (!_spotLights.empty()) { + _spotLights.clear(); + } +} + + +void DeferredLightingEffect::copyBack(RenderArgs* args) { + auto framebufferCache = DependencyManager::get(); + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + batch.enableStereo(false); + QSize framebufferSize = framebufferCache->getFrameBufferSize(); + + // TODO why doesn't this blit work? It only seems to affect a small area below the rear view mirror. + // auto destFbo = framebufferCache->getPrimaryFramebuffer(); + auto destFbo = framebufferCache->getPrimaryFramebufferDepthColor(); + // gpu::Vec4i vp = args->_viewport; + // batch.blit(_copyFBO, vp, framebufferCache->getPrimaryFramebuffer(), vp); + batch.setFramebuffer(destFbo); + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.setViewTransform(Transform()); + { + float sMin = args->_viewport.x / (float)framebufferSize.width(); + float sWidth = args->_viewport.z / (float)framebufferSize.width(); + float tMin = args->_viewport.y / (float)framebufferSize.height(); + float tHeight = args->_viewport.w / (float)framebufferSize.height(); + Transform model; + batch.setPipeline(_blitLightBuffer); + model.setTranslation(glm::vec3(sMin, tMin, 0.0)); + model.setScale(glm::vec3(sWidth, tHeight, 1.0)); + batch.setModelTransform(model); + } + + batch.setResourceTexture(0, _copyFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + args->_context->render(batch); + }); + framebufferCache->releaseFramebuffer(_copyFBO); +} + +void DeferredLightingEffect::setupTransparent(RenderArgs* args, int lightBufferUnit) { + auto globalLight = _allocatedLights[_globalLights.front()]; + args->_batch->setUniformBuffer(lightBufferUnit, globalLight->getSchemaBuffer()); +} + +static void loadLightProgram(const char* vertSource, const char* fragSource, bool lightVolume, gpu::PipelinePointer& pipeline, LightLocationsPtr& locations) { + auto VS = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(vertSource))); + auto PS = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(fragSource))); + + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PS)); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), 0)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); + slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), 3)); + slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), 4)); + slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), 5)); + const int LIGHT_GPU_SLOT = 3; + slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), LIGHT_GPU_SLOT)); + const int ATMOSPHERE_GPU_SLOT = 4; + slotBindings.insert(gpu::Shader::Binding(std::string("atmosphereBufferUnit"), ATMOSPHERE_GPU_SLOT)); + + slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), DeferredLightingEffect::DEFERRED_TRANSFORM_BUFFER_SLOT)); + + gpu::Shader::makeProgram(*program, slotBindings); + + locations->shadowDistances = program->getUniforms().findLocation("shadowDistances"); + locations->shadowScale = program->getUniforms().findLocation("shadowScale"); + + locations->radius = program->getUniforms().findLocation("radius"); + locations->ambientSphere = program->getUniforms().findLocation("ambientSphere.L00"); + + locations->texcoordMat = program->getUniforms().findLocation("texcoordMat"); + locations->coneParam = program->getUniforms().findLocation("coneParam"); + + locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); + locations->atmosphereBufferUnit = program->getBuffers().findLocation("atmosphereBufferUnit"); + locations->deferredTransformBuffer = program->getBuffers().findLocation("deferredTransformBuffer"); + + auto state = std::make_shared(); + if (lightVolume) { + state->setCullMode(gpu::State::CULL_BACK); + + // No need for z test since the depth buffer is not bound state->setDepthTest(true, false, gpu::LESS_EQUAL); + // TODO: We should bind the true depth buffer both as RT and texture for the depth test + // TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases + state->setDepthClampEnable(true); + + // additive blending + state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + } else { + state->setCullMode(gpu::State::CULL_BACK); + } + pipeline.reset(gpu::Pipeline::create(program, state)); + +} + +void DeferredLightingEffect::setAmbientLightMode(int preset) { + if ((preset >= 0) && (preset < gpu::SphericalHarmonics::NUM_PRESET)) { + _ambientLightMode = preset; + auto light = _allocatedLights.front(); + light->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset(preset % gpu::SphericalHarmonics::NUM_PRESET)); + } else { + // force to preset 0 + setAmbientLightMode(0); + } +} + +void DeferredLightingEffect::setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity, float ambientIntensity) { + auto light = _allocatedLights.front(); + light->setDirection(direction); + light->setColor(diffuse); + light->setIntensity(intensity); + light->setAmbientIntensity(ambientIntensity); +} + +void DeferredLightingEffect::setGlobalSkybox(const model::SkyboxPointer& skybox) { + _skybox = skybox; +} + +model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { + if (!_spotLightMesh) { + _spotLightMesh = std::make_shared(); + + int slices = 32; + int rings = 3; + int vertices = 2 + rings * slices; + int originVertex = vertices - 2; + int capVertex = vertices - 1; + int verticesSize = vertices * 3 * sizeof(float); + int indices = 3 * slices * (1 + 1 + 2 * (rings -1)); + int ringFloatOffset = slices * 3; + + + float* vertexData = new float[verticesSize]; + float* vertexRing0 = vertexData; + float* vertexRing1 = vertexRing0 + ringFloatOffset; + float* vertexRing2 = vertexRing1 + ringFloatOffset; + + for (int i = 0; i < slices; i++) { + float theta = TWO_PI * i / slices; + auto cosin = glm::vec2(cosf(theta), sinf(theta)); + + *(vertexRing0++) = cosin.x; + *(vertexRing0++) = cosin.y; + *(vertexRing0++) = 0.0f; + + *(vertexRing1++) = cosin.x; + *(vertexRing1++) = cosin.y; + *(vertexRing1++) = 0.33f; + + *(vertexRing2++) = cosin.x; + *(vertexRing2++) = cosin.y; + *(vertexRing2++) = 0.66f; + } + + *(vertexRing2++) = 0.0f; + *(vertexRing2++) = 0.0f; + *(vertexRing2++) = -1.0f; + + *(vertexRing2++) = 0.0f; + *(vertexRing2++) = 0.0f; + *(vertexRing2++) = 1.0f; + + _spotLightMesh->setVertexBuffer(gpu::BufferView(new gpu::Buffer(verticesSize, (gpu::Byte*) vertexData), gpu::Element::VEC3F_XYZ)); + delete[] vertexData; + + gpu::uint16* indexData = new gpu::uint16[indices]; + gpu::uint16* index = indexData; + for (int i = 0; i < slices; i++) { + *(index++) = originVertex; + + int s0 = i; + int s1 = ((i + 1) % slices); + *(index++) = s0; + *(index++) = s1; + + int s2 = s0 + slices; + int s3 = s1 + slices; + *(index++) = s1; + *(index++) = s0; + *(index++) = s2; + + *(index++) = s1; + *(index++) = s2; + *(index++) = s3; + + int s4 = s2 + slices; + int s5 = s3 + slices; + *(index++) = s3; + *(index++) = s2; + *(index++) = s4; + + *(index++) = s3; + *(index++) = s4; + *(index++) = s5; + + + *(index++) = s5; + *(index++) = s4; + *(index++) = capVertex; + } + + _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); + delete[] indexData; + + model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES); + //DEBUG: model::Mesh::Part part(0, indices, 0, model::Mesh::LINE_STRIP); + + _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL)); + + _spotLightMesh->getVertexStream(); + } + return _spotLightMesh; +} + diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 73a487c2df..df062e3fa9 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -231,8 +231,8 @@ const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() { auto state = std::make_shared(); state->setDepthTest(false); - // additive blending - state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + // additive blending + state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); _opaquePipeline.reset(gpu::Pipeline::create(program, state)); } @@ -336,7 +336,7 @@ void DrawStencilDeferred::run(const SceneContextPointer& sceneContext, const Ren batch.setPipeline(getOpaquePipeline()); - batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.draw(gpu::TRIANGLE_STRIP, 4); batch.setResourceTexture(0, nullptr); }); diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index b34ed3ed2b..63992c35ad 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -1,70 +1,70 @@ - -<@if not SKINNING_SLH@> -<@def SKINNING_SLH@> - -const int MAX_TEXCOORDS = 2; -const int MAX_CLUSTERS = 128; -const int INDICES_PER_VERTEX = 4; - -layout(std140) uniform skinClusterBuffer { - mat4 clusterMatrices[MAX_CLUSTERS]; -}; - -void skinPosition(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - } - - skinnedPosition = newPosition; -} - -void skinPositionNormal(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, - out vec4 skinnedPosition, out vec3 skinnedNormal) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - } - - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; -} - -void skinPositionNormalTangent(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, - out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { - vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); - - for (int i = 0; i < INDICES_PER_VERTEX; i++) { - mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; - float clusterWeight = skinClusterWeight[i]; - newPosition += clusterMatrix * inPosition * clusterWeight; - newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; - newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; - } - - skinnedPosition = newPosition; - skinnedNormal = newNormal.xyz; - skinnedTangent = newTangent.xyz; -} - - + +<@if not SKINNING_SLH@> +<@def SKINNING_SLH@> + +const int MAX_TEXCOORDS = 2; +const int MAX_CLUSTERS = 128; +const int INDICES_PER_VERTEX = 4; + +layout(std140) uniform skinClusterBuffer { + mat4 clusterMatrices[MAX_CLUSTERS]; +}; + +void skinPosition(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + } + + skinnedPosition = newPosition; +} + +void skinPositionNormal(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; +} + +void skinPositionNormalTangent(vec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 newTangent = vec4(0.0, 0.0, 0.0, 0.0); + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + newPosition += clusterMatrix * inPosition * clusterWeight; + newNormal += clusterMatrix * vec4(inNormal.xyz, 0.0) * clusterWeight; + newTangent += clusterMatrix * vec4(inTangent.xyz, 0.0) * clusterWeight; + } + + skinnedPosition = newPosition; + skinnedNormal = newNormal.xyz; + skinnedTangent = newTangent.xyz; +} + + <@endif@> \ No newline at end of file diff --git a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf b/libraries/render-utils/src/directional_ambient_light_shadow_map.slf index 2dee8bf9b9..97d69f2e63 100755 --- a/libraries/render-utils/src/directional_ambient_light_shadow_map.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow_map.slf @@ -20,7 +20,7 @@ <$declareEvalAmbientSphereGlobalColor()$> // Everything about shadow -<@include Shadow.slh@> +<@include Shadow.slh@> in vec2 _texCoord0; out vec4 _fragColor; diff --git a/libraries/render-utils/src/directional_light_shadow_map.slf b/libraries/render-utils/src/directional_light_shadow_map.slf index b3362a2040..4249b2787c 100644 --- a/libraries/render-utils/src/directional_light_shadow_map.slf +++ b/libraries/render-utils/src/directional_light_shadow_map.slf @@ -21,7 +21,7 @@ <$declareEvalAmbientGlobalColor()$> // Everything about shadow -<@include Shadow.slh@> +<@include Shadow.slh@> in vec2 _texCoord0; out vec4 _fragColor; diff --git a/libraries/render-utils/src/directional_skybox_light_shadow_map.slf b/libraries/render-utils/src/directional_skybox_light_shadow_map.slf index 632270476b..6f709f31fa 100755 --- a/libraries/render-utils/src/directional_skybox_light_shadow_map.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow_map.slf @@ -21,7 +21,7 @@ <$declareEvalSkyboxGlobalColor()$> // Everything about shadow -<@include Shadow.slh@> +<@include Shadow.slh@> in vec2 _texCoord0; out vec4 _fragColor; diff --git a/libraries/render-utils/src/model_normal_map.slv b/libraries/render-utils/src/model_normal_map.slv index 752f367a48..e989c1c294 100755 --- a/libraries/render-utils/src/model_normal_map.slv +++ b/libraries/render-utils/src/model_normal_map.slv @@ -1,44 +1,44 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// model_normal_map.vert -// vertex shader -// -// Created by Andrzej Kapolka on 10/14/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 -// - -<@include gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -const int MAX_TEXCOORDS = 2; - -uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; - -out vec4 _position; -out vec2 _texCoord0; -out vec3 _normal; -out vec3 _tangent; -out vec3 _color; - -void main(void) { - // pass along the diffuse color - _color = inColor.rgb; - - // and the texture coordinates - _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.xy, 0.0, 1.0)).st; - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> - <$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$> - <$transformModelToEyeDir(cam, obj, inTangent.xyz, _tangent)$> -} +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_normal_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/14/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 +// + +<@include gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +const int MAX_TEXCOORDS = 2; + +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; + +out vec4 _position; +out vec2 _texCoord0; +out vec3 _normal; +out vec3 _tangent; +out vec3 _color; + +void main(void) { + // pass along the diffuse color + _color = inColor.rgb; + + // and the texture coordinates + _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.xy, 0.0, 1.0)).st; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$> + <$transformModelToEyeDir(cam, obj, inTangent.xyz, _tangent)$> +} diff --git a/libraries/render-utils/src/model_shadow.slv b/libraries/render-utils/src/model_shadow.slv index b18c123d82..c81235b392 100755 --- a/libraries/render-utils/src/model_shadow.slv +++ b/libraries/render-utils/src/model_shadow.slv @@ -1,26 +1,26 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// model_shadow.vert -// vertex shader -// -// Created by Andrzej Kapolka on 3/24/14. -// 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 gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -void main(void) { - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> -} +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_shadow.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/24/14. +// 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 gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +void main(void) { + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> +} diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index bb2058b5f8..cba419dd4d 100755 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -1,48 +1,48 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// skin_model.vert -// vertex shader -// -// Created by Andrzej Kapolka on 10/14/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 -// - -<@include gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -<@include Skinning.slh@> - -uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; - -out vec4 _position; -out vec2 _texCoord0; -out vec3 _normal; -out vec3 _color; - -void main(void) { - vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0); - - skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); - - // pass along the diffuse color - _color = inColor.rgb; - - // and the texture coordinates - _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> - <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> - _normal = interpolatedNormal.xyz; -} +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/14/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 +// + +<@include gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +<@include Skinning.slh@> + +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; + +out vec4 _position; +out vec2 _texCoord0; +out vec3 _normal; +out vec3 _color; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec3 interpolatedNormal = vec3(0.0, 0.0, 0.0); + + skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); + + // pass along the diffuse color + _color = inColor.rgb; + + // and the texture coordinates + _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> + <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> + _normal = interpolatedNormal.xyz; +} diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index f198cb5f77..5dfe5ba957 100755 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -1,56 +1,56 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// skin_model_normal_map.vert -// vertex shader -// -// Created by Andrzej Kapolka on 10/29/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 -// - -<@include gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -<@include Skinning.slh@> - -uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; - -out vec4 _position; -out vec2 _texCoord0; -out vec3 _normal; -out vec3 _tangent; -out vec3 _color; - -void main(void) { - vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); - vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); - - skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); - - // pass along the diffuse color - _color = inColor.rgb; - - // and the texture coordinates - _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; - - interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); - interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> - <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> - <$transformModelToEyeDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> - - _normal = interpolatedNormal.xyz; - _tangent = interpolatedTangent.xyz; -} +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_normal_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 10/29/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 +// + +<@include gpu/Inputs.slh@> + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +<@include Skinning.slh@> + +uniform mat4 texcoordMatrices[MAX_TEXCOORDS]; + +out vec4 _position; +out vec2 _texCoord0; +out vec3 _normal; +out vec3 _tangent; +out vec3 _color; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + vec4 interpolatedNormal = vec4(0.0, 0.0, 0.0, 0.0); + vec4 interpolatedTangent = vec4(0.0, 0.0, 0.0, 0.0); + + skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); + + // pass along the diffuse color + _color = inColor.rgb; + + // and the texture coordinates + _texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st; + + interpolatedNormal = vec4(normalize(interpolatedNormal.xyz), 0.0); + interpolatedTangent = vec4(normalize(interpolatedTangent.xyz), 0.0); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$> + <$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$> + <$transformModelToEyeDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$> + + _normal = interpolatedNormal.xyz; + _tangent = interpolatedTangent.xyz; +} diff --git a/libraries/render-utils/src/skin_model_shadow.slv b/libraries/render-utils/src/skin_model_shadow.slv index 9c00f9065b..6684cfea80 100755 --- a/libraries/render-utils/src/skin_model_shadow.slv +++ b/libraries/render-utils/src/skin_model_shadow.slv @@ -1,29 +1,29 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// skin_model_shadow.vert -// vertex shader -// -// Created by Andrzej Kapolka on 3/24/14. -// 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 gpu/Inputs.slh@> -<@include gpu/Transform.slh@> -<$declareStandardTransform()$> - -<@include Skinning.slh@> - -void main(void) { - vec4 position = vec4(0.0, 0.0, 0.0, 0.0); - skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position); - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, position, gl_Position)$> -} +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// skin_model_shadow.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/24/14. +// 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 gpu/Inputs.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include Skinning.slh@> + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + skinPosition(inSkinClusterIndex, inSkinClusterWeight, inPosition, position); + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, position, gl_Position)$> +} diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 32cd21d0bc..57e21a1511 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -1,163 +1,163 @@ -// -// DrawStatus.cpp -// render/src/render -// -// Created by Niraj Venkat on 6/29/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 "DrawStatus.h" - -#include -#include - -#include -#include -#include - -#include - -#include "drawItemBounds_vert.h" -#include "drawItemBounds_frag.h" -#include "drawItemStatus_vert.h" -#include "drawItemStatus_frag.h" - -using namespace render; - - - -const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() { - if (!_drawItemBoundsPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag))); - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - - gpu::Shader::BindingSet slotBindings; - gpu::Shader::makeProgram(*program, slotBindings); - - _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); - _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); - - auto state = std::make_shared(); - - state->setDepthTest(true, false, gpu::LESS_EQUAL); - - // Blend on transparent - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); - - // Good to go add the brand new pipeline - _drawItemBoundsPipeline.reset(gpu::Pipeline::create(program, state)); - } - return _drawItemBoundsPipeline; -} - -const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() { - if (!_drawItemStatusPipeline) { - auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert))); - auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag))); - gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); - - gpu::Shader::BindingSet slotBindings; - gpu::Shader::makeProgram(*program, slotBindings); - - _drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos"); - _drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim"); - _drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus"); - - auto state = std::make_shared(); - - state->setDepthTest(false, false, gpu::LESS_EQUAL); - - // Blend on transparent - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); - - // Good to go add the brand new pipeline - _drawItemStatusPipeline.reset(gpu::Pipeline::create(program, state)); - } - return _drawItemStatusPipeline; -} - -void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { - assert(renderContext->args); - assert(renderContext->args->_viewFrustum); - RenderArgs* args = renderContext->args; - auto& scene = sceneContext->_scene; - - // FIrst thing, we collect the bound and the status for all the items we want to render - int nbItems = 0; - { - if (!_itemBounds) { - _itemBounds = std::make_shared(); - } - if (!_itemStatus) { - _itemStatus = std::make_shared();; - } - - _itemBounds->resize((inItems.size() * sizeof(AABox))); - _itemStatus->resize((inItems.size() * sizeof(glm::vec4))); - AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); - glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); - for (auto& item : inItems) { - if (!item.bounds.isInvalid()) { - if (!item.bounds.isNull()) { - (*itemAABox) = item.bounds; - } else { - (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); - } - auto& itemScene = scene->getItem(item.id); - (*itemStatus) = itemScene.getStatusPackedValues(); - - nbItems++; - itemAABox++; - itemStatus++; - } - } - } - - if (nbItems == 0) { - return; - } - - // Allright, something to render let's do it - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { - glm::mat4 projMat; - Transform viewMat; - args->_viewFrustum->evalProjectionMatrix(projMat); - args->_viewFrustum->evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setModelTransform(Transform()); - - // bind the one gpu::Pipeline we need - batch.setPipeline(getDrawItemBoundsPipeline()); - - AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); - glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); - - const unsigned int VEC3_ADRESS_OFFSET = 3; - - for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); - batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - - batch.draw(gpu::LINES, 24, 0); - } - - batch.setPipeline(getDrawItemStatusPipeline()); - for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); - batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); - - batch.draw(gpu::TRIANGLES, 24, 0); - } - }); -} +// +// DrawStatus.cpp +// render/src/render +// +// Created by Niraj Venkat on 6/29/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 "DrawStatus.h" + +#include +#include + +#include +#include +#include + +#include + +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" +#include "drawItemStatus_vert.h" +#include "drawItemStatus_frag.h" + +using namespace render; + + + +const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() { + if (!_drawItemBoundsPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + + auto state = std::make_shared(); + + state->setDepthTest(true, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + // Good to go add the brand new pipeline + _drawItemBoundsPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _drawItemBoundsPipeline; +} + +const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() { + if (!_drawItemStatusPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus"); + + auto state = std::make_shared(); + + state->setDepthTest(false, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + // Good to go add the brand new pipeline + _drawItemStatusPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _drawItemStatusPipeline; +} + +void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + // FIrst thing, we collect the bound and the status for all the items we want to render + int nbItems = 0; + { + if (!_itemBounds) { + _itemBounds = std::make_shared(); + } + if (!_itemStatus) { + _itemStatus = std::make_shared();; + } + + _itemBounds->resize((inItems.size() * sizeof(AABox))); + _itemStatus->resize((inItems.size() * sizeof(glm::vec4))); + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + for (auto& item : inItems) { + if (!item.bounds.isInvalid()) { + if (!item.bounds.isNull()) { + (*itemAABox) = item.bounds; + } else { + (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); + } + auto& itemScene = scene->getItem(item.id); + (*itemStatus) = itemScene.getStatusPackedValues(); + + nbItems++; + itemAABox++; + itemStatus++; + } + } + } + + if (nbItems == 0) { + return; + } + + // Allright, something to render let's do it + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // bind the one gpu::Pipeline we need + batch.setPipeline(getDrawItemBoundsPipeline()); + + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + + const unsigned int VEC3_ADRESS_OFFSET = 3; + + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + + batch.draw(gpu::LINES, 24, 0); + } + + batch.setPipeline(getDrawItemStatusPipeline()); + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); + + batch.draw(gpu::TRIANGLES, 24, 0); + } + }); +} diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index ca4763d33b..a96b897f5c 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -1,43 +1,43 @@ -// -// DrawStatus.h -// render/src/render -// -// Created by Niraj Venkat on 6/29/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 -// - -#ifndef hifi_render_DrawStatus_h -#define hifi_render_DrawStatus_h - -#include "DrawTask.h" -#include "gpu/Batch.h" - -namespace render { - class DrawStatus { - int _drawItemBoundPosLoc = -1; - int _drawItemBoundDimLoc = -1; - int _drawItemStatusPosLoc = -1; - int _drawItemStatusDimLoc = -1; - int _drawItemStatusValueLoc = -1; - - gpu::Stream::FormatPointer _drawItemFormat; - gpu::PipelinePointer _drawItemBoundsPipeline; - gpu::PipelinePointer _drawItemStatusPipeline; - gpu::BufferPointer _itemBounds; - gpu::BufferPointer _itemStatus; - - public: - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); - - typedef Job::ModelI JobModel; - - const gpu::PipelinePointer& getDrawItemBoundsPipeline(); - const gpu::PipelinePointer& getDrawItemStatusPipeline(); - }; -} - -#endif // hifi_render_DrawStatus_h +// +// DrawStatus.h +// render/src/render +// +// Created by Niraj Venkat on 6/29/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 +// + +#ifndef hifi_render_DrawStatus_h +#define hifi_render_DrawStatus_h + +#include "DrawTask.h" +#include "gpu/Batch.h" + +namespace render { + class DrawStatus { + int _drawItemBoundPosLoc = -1; + int _drawItemBoundDimLoc = -1; + int _drawItemStatusPosLoc = -1; + int _drawItemStatusDimLoc = -1; + int _drawItemStatusValueLoc = -1; + + gpu::Stream::FormatPointer _drawItemFormat; + gpu::PipelinePointer _drawItemBoundsPipeline; + gpu::PipelinePointer _drawItemStatusPipeline; + gpu::BufferPointer _itemBounds; + gpu::BufferPointer _itemStatus; + + public: + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); + + typedef Job::ModelI JobModel; + + const gpu::PipelinePointer& getDrawItemBoundsPipeline(); + const gpu::PipelinePointer& getDrawItemStatusPipeline(); + }; +} + +#endif // hifi_render_DrawStatus_h diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv index 4cb9a82371..7b69370f13 100644 --- a/libraries/render/src/render/drawItemBounds.slv +++ b/libraries/render/src/render/drawItemBounds.slv @@ -19,39 +19,39 @@ uniform vec3 inBoundPos; uniform vec3 inBoundDim; -void main(void) { - const vec4 UNIT_BOX[8] = vec4[8]( - vec4(0.0, 0.0, 0.0, 1.0), - vec4(1.0, 0.0, 0.0, 1.0), - vec4(0.0, 1.0, 0.0, 1.0), - vec4(1.0, 1.0, 0.0, 1.0), - vec4(0.0, 0.0, 1.0, 1.0), - vec4(1.0, 0.0, 1.0, 1.0), - vec4(0.0, 1.0, 1.0, 1.0), - vec4(1.0, 1.0, 1.0, 1.0) - ); - const int UNIT_BOX_LINE_INDICES[24] = int[24]( - 0, 1, - 1, 3, - 3, 2, - 2, 0, - 4, 5, - 5, 7, - 7, 6, - 6, 4, - 2, 6, - 3, 7, - 0, 4, - 1, 5 - ); - vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; - - pos.xyz = inBoundPos + inBoundDim * pos.xyz; - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, pos, gl_Position)$> - - // varTexcoord = (pos.xy + 1) * 0.5; +void main(void) { + const vec4 UNIT_BOX[8] = vec4[8]( + vec4(0.0, 0.0, 0.0, 1.0), + vec4(1.0, 0.0, 0.0, 1.0), + vec4(0.0, 1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0), + vec4(0.0, 0.0, 1.0, 1.0), + vec4(1.0, 0.0, 1.0, 1.0), + vec4(0.0, 1.0, 1.0, 1.0), + vec4(1.0, 1.0, 1.0, 1.0) + ); + const int UNIT_BOX_LINE_INDICES[24] = int[24]( + 0, 1, + 1, 3, + 3, 2, + 2, 0, + 4, 5, + 5, 7, + 7, 6, + 6, 4, + 2, 6, + 3, 7, + 0, 4, + 1, 5 + ); + vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; + + pos.xyz = inBoundPos + inBoundDim * pos.xyz; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, pos, gl_Position)$> + + // varTexcoord = (pos.xy + 1) * 0.5; } \ No newline at end of file diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index b90daef76a..178293575d 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -1,103 +1,103 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// drawItemStatus.slv -// vertex shader -// -// Created by Sam Gateau on 6/30/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 -// - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -out vec4 varColor; - -uniform vec3 inBoundPos; -uniform vec3 inBoundDim; -uniform ivec4 inStatus; - -vec3 paintRainbow(float normalizedHue) { - float v = normalizedHue * 6.f; - if (v < 0.f) { - return vec3(1.f, 0.f, 0.f); - } else if (v < 1.f) { - return vec3(1.f, v, 0.f); - } else if (v < 2.f) { - return vec3(1.f - (v-1.f), 1.f, 0.f); - } else if (v < 3.f) { - return vec3(0.f, 1.f, (v-2.f)); - } else if (v < 4.f) { - return vec3(0.f, 1.f - (v-3.f), 1.f ); - } else if (v < 5.f) { - return vec3((v-4.f), 0.f, 1.f ); - } else if (v < 6.f) { - return vec3(1.f, 0.f, 1.f - (v-5.f)); - } else { - return vec3(1.f, 0.f, 0.f); - } -} - -vec2 unpackStatus(int v) { - return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), - clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0)); -} - -void main(void) { - const vec2 ICON_PIXEL_SIZE = vec2(10, 10); - const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); - const int NUM_VERTICES = 6; - const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES]( - vec4(-1.0, -1.0, 0.0, 1.0), - vec4(1.0, -1.0, 0.0, 1.0), - vec4(-1.0, 1.0, 0.0, 1.0), - vec4(-1.0, 1.0, 0.0, 1.0), - vec4(1.0, -1.0, 0.0, 1.0), - vec4(1.0, 1.0, 0.0, 1.0) - ); - - // anchor point in clip space - vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0); - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$> - - // Which icon are we dealing with ? - int iconNum = gl_VertexID / NUM_VERTICES; - - // if invalid, just kill - if (inStatus[iconNum] == 0xFFFFFFFF) { - gl_Position = anchorPoint; - varColor = vec4(1.0); - return; - } - - // unpack to get x and y satus - vec2 iconStatus = unpackStatus(inStatus[iconNum]); - - // Use the status for showing a color - varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); - - // Also changes the size of the notification - vec2 iconScale = ICON_PIXEL_SIZE; - iconScale = max(vec2(1, 1), (iconScale * iconStatus.x)); - - //Offset icon to the right based on the iconNum - vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); - - // Final position in pixel space - int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; - vec4 pos = UNIT_QUAD[twoTriID]; - vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale; - - vec4 viewport; - <$transformCameraViewport(cam, viewport)$>; - vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w); - gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0)); - +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// drawItemStatus.slv +// vertex shader +// +// Created by Sam Gateau on 6/30/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 +// + +<@include gpu/Transform.slh@> + +<$declareStandardTransform()$> + +out vec4 varColor; + +uniform vec3 inBoundPos; +uniform vec3 inBoundDim; +uniform ivec4 inStatus; + +vec3 paintRainbow(float normalizedHue) { + float v = normalizedHue * 6.f; + if (v < 0.f) { + return vec3(1.f, 0.f, 0.f); + } else if (v < 1.f) { + return vec3(1.f, v, 0.f); + } else if (v < 2.f) { + return vec3(1.f - (v-1.f), 1.f, 0.f); + } else if (v < 3.f) { + return vec3(0.f, 1.f, (v-2.f)); + } else if (v < 4.f) { + return vec3(0.f, 1.f - (v-3.f), 1.f ); + } else if (v < 5.f) { + return vec3((v-4.f), 0.f, 1.f ); + } else if (v < 6.f) { + return vec3(1.f, 0.f, 1.f - (v-5.f)); + } else { + return vec3(1.f, 0.f, 0.f); + } +} + +vec2 unpackStatus(int v) { + return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), + clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0)); +} + +void main(void) { + const vec2 ICON_PIXEL_SIZE = vec2(10, 10); + const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); + const int NUM_VERTICES = 6; + const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES]( + vec4(-1.0, -1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0) + ); + + // anchor point in clip space + vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0); + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$> + + // Which icon are we dealing with ? + int iconNum = gl_VertexID / NUM_VERTICES; + + // if invalid, just kill + if (inStatus[iconNum] == 0xFFFFFFFF) { + gl_Position = anchorPoint; + varColor = vec4(1.0); + return; + } + + // unpack to get x and y satus + vec2 iconStatus = unpackStatus(inStatus[iconNum]); + + // Use the status for showing a color + varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); + + // Also changes the size of the notification + vec2 iconScale = ICON_PIXEL_SIZE; + iconScale = max(vec2(1, 1), (iconScale * iconStatus.x)); + + //Offset icon to the right based on the iconNum + vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); + + // Final position in pixel space + int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; + vec4 pos = UNIT_QUAD[twoTriID]; + vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale; + + vec4 viewport; + <$transformCameraViewport(cam, viewport)$>; + vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w); + gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0)); + } \ No newline at end of file diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp index a8d602e6aa..59d2765659 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.cpp +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -90,8 +90,8 @@ void SceneScriptingInterface::setStageSunModelEnable(bool isEnabled) { _skyStage->setSunModelEnable(isEnabled); } -bool SceneScriptingInterface::isStageSunModelEnabled() const { - return _skyStage->isSunModelEnabled(); +bool SceneScriptingInterface::isStageSunModelEnabled() const { + return _skyStage->isSunModelEnabled(); } void SceneScriptingInterface::setBackgroundMode(const QString& mode) { diff --git a/tools/scribe/src/TextTemplate.cpp b/tools/scribe/src/TextTemplate.cpp index e7537508bc..027a73da5d 100755 --- a/tools/scribe/src/TextTemplate.cpp +++ b/tools/scribe/src/TextTemplate.cpp @@ -1,989 +1,989 @@ -// -// TextTemplate.cpp -// tools/shaderScribe/src -// -// Created by Sam Gateau on 12/15/2014. -// 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 -#include "TextTemplate.h" - -#include -#include -#include -#include - -typedef TextTemplate::Block::Pointer BlockPointer; -typedef TextTemplate::Config::Pointer ConfigPointer; -typedef TextTemplate::Pointer TextTemplatePointer; - -//----------------------------------------------------------------------------- -TextTemplate::Config::Config() : - _includes(), - _funcs(), - _logStream(&std::cout), - _numErrors(0), - _includerCallback(TextTemplate::loadFile) { - _paths.push_back(""); -} - -const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) { - if (!config) { - return TextTemplatePointer(); - } - - TextTemplatePointer included = config->findInclude(include); - if (included) { - return included; - } - - // INcluded doest exist yet so let's try to create it - String includeStream; - if (config->_includerCallback(config, include, includeStream)) { - // ok, then create a new Template on the include file with this as lib - included = std::make_shared(include, config); - - std::stringstream src(includeStream); - - int nbErrors = included->parse(src); - if (nbErrors > 0) { - included->logError(included->_root, "File failed to parse, not included"); - return TextTemplatePointer(); - } - config->_includes.insert(Includes::value_type(include, included)); - return included; - } - - return TextTemplatePointer(); -} - -const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) { - Includes::iterator includeIt = _includes.find(String(include)); - if (includeIt != _includes.end()) { - return (*includeIt).second; - } else { - return TextTemplatePointer(); - } -} - -void TextTemplate::Config::addIncludePath(const char* path) { - _paths.push_back(String(path)); -} - -bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) { - String sourceFile(filename); - String fullfilename; - for (unsigned int i = 0; i < config->_paths.size(); i++) { - fullfilename = config->_paths[i] + sourceFile; - std::ifstream ifs; - ifs.open(fullfilename.c_str()); - if (ifs.is_open()) { - std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); - source = str; - ifs.close(); - return (source.length() > 0); - } - } - - return false; -} - -TextTemplate::Funcs::Funcs() : - _funcs() { -} - -TextTemplate::Funcs::~Funcs() { -} - - -const BlockPointer TextTemplate::Funcs::findFunc(const char* func) { - map::iterator it = _funcs.find(String(func)); - if (it != _funcs.end()) { - return (*it).second; - } else { - return BlockPointer(); - } -} - -const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) { - BlockPointer included = findFunc(func); - if (! included) { - _funcs.insert(map::value_type(func, funcBlock)); - } - return included; -} - -void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) { - if (parent) { - parent->blocks.push_back(block); - block->parent = parent; - } -} - -const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) { - if (block && block->command.isBlockEnd()) { - return block->parent; - } else { - return block; - } -} - -void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) { - va_list argp; - va_start(argp, fmt); - - char buff[256]; - sprintf(buff, fmt, argp); - - _numErrors++; - log() << block->sourceName << " Error >>" << buff << std::endl; - - int level = 1; - displayTree(std::cerr, level); - -} - -bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { - std::stringstream dst; - while (!str->eof()) { - - std::string datatoken; - getline((*str), datatoken, Tag::BEGIN); - dst << datatoken; - - char next = str->peek(); - if (next == Tag::VAR) { - tagType = Tag::VARIABLE; - grabbed = dst.str(); - str->get(); // skip tag char - return true; - } else if (next == Tag::COM) { - tagType = Tag::COMMAND; - grabbed = dst.str(); - str->get(); // skip tag char - return true; - } else if (next == Tag::REM) { - tagType = Tag::REMARK; - grabbed = dst.str(); - str->get(); // skip tag char - return true; - } else { - if (!str->eof()) { - // false positive, just found the Tag::BEGIN without consequence - dst << Tag::BEGIN; - // keep searching - } else { - // end of the file finishing with no tag - tagType = Tag::INVALID; - grabbed = dst.str(); - return true; - } - } - } - - return false; -} - -bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { - std::stringstream dst; - - // preEnd char depends on tag type - char preEnd = Tag::COM; - if (tagType == Tag::VARIABLE) { - preEnd = Tag::VAR; - } else if (tagType == Tag::REMARK) { - preEnd = Tag::REM; - } - - while (!str->eof() && !str->fail()) { - // looking for the end of the tag means find the next preEnd - std::string dataToken; - getline((*str), dataToken, preEnd); - char end = str->peek(); - - dst << dataToken; - - // and if the next char is Tag::END then that's it - if (end == Tag::END) { - grabbed = dst.str(); - str->get(); // eat the Tag::END - return true; - } else { - // false positive, keep on searching - dst << preEnd; - } - } - grabbed = dst.str(); - return false; -} - -bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType, - Tag::Type& nextTagType) { - if (str->eof()) { - return false; - } - - if (!_steppingStarted) { - _steppingStarted = true; - return grabUntilBeginTag(str, grabbed, nextTagType); - } - - // Read from the last opening Tag::BEGIN captured to the next Tag::END - if (grabUntilEndTag(str, tag, tagType)) { - // skip trailing space and new lines only after Command or Remark tag block - if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) { - while (!str->eof() && !str->fail()) { - char c = str->peek(); - if ((c == ' ') || (c == '\t') || (c == '\n')) { - str->get(); - } else { - break; - } - } - } - - grabUntilBeginTag(str, grabbed, nextTagType); - } - - return true; //hasElement; -} - -const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag, - Tag::Type& tagType) { - switch (tagType) { - case Tag::INVALID: - block->ostr << grabbed; - return block; - break; - case Tag::VARIABLE: - return processStepVar(block, grabbed, tag); - break; - case Tag::COMMAND: - return processStepCommand(block, grabbed, tag); - break; - case Tag::REMARK: - return processStepRemark(block, grabbed, tag); - break; - } - - logError(block, "Invalid tag"); - return block; -} - -bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) { - bool goOn = true; - std::string::size_type i = 0; - while (goOn && (i < src.length())) { - char c = src[i]; - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) { - token += c; - } else { - if (!token.empty()) { - reminder = src.substr(i); - return true; - } - } - i++; - } - - return (!token.empty()); -} - -bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) { - std::stringstream str(src); - String token; - - while (!str.eof()) { - str >> token; - if (!str.fail()) { - arguments.push_back(token); - } - } - - return true; -} - -bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) { - if (src.empty()) { - return false; - } - - std::stringstream argstr(src); - std::stringstream dest; - bool inVar = false; - while (!argstr.eof()) { - // parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END - String token; - char tag; - if (!inVar) { - getline(argstr, token, Tag::BEGIN); - dest << token; - tag = argstr.peek(); - } else { - getline(argstr, token, Tag::END); - dest << token; - tag = token.back(); - } - - if (tag == Tag::VAR) { - if (!inVar) { - // real var coming: - arguments.push_back(dest.str()); - inVar = true; - } else { - // real var over - arguments.push_back(dest.str()); - inVar = false; - } - } else { - if (argstr.eof()) { - arguments.push_back(dest.str()); - } else { - // put back the tag char stolen - dest << (!inVar ? Tag::BEGIN : Tag::END); - } - } - } - - return true; -} - -bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) { - if (src.empty()) { - return false; - } - std::stringstream streamSrc(src); - String params; - getline(streamSrc, params, '('); - getline(streamSrc, params, ')'); - if (params.empty()) { - return false; - } - - std::stringstream str(params); - String token; - int nbTokens = 0; - while (!str.eof()) { - char c = str.peek(); - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) { - token += c; - } else if (c == ',') { - if (!token.empty()) { - arguments.push_back(token); - nbTokens++; - } - token.clear(); - } - - str.get(); - } - - if (token.size()) { - arguments.push_back(token); - nbTokens++; - } - - return (nbTokens > 0); -} - -const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) { - // then look at the define - String var = tag; - String varName; - String val; - - if (grabFirstToken(var, varName, val)) { - if (!varName.empty()) { - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::VAR; - - newBlock->command.arguments.push_back(varName); - - if (!val.empty()) { - convertExpressionToFuncArguments(val, newBlock->command.arguments); - } - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; - } - } - - return block; -} - -const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) { - // Grab the command name - String command = tag; - String commandName; - - if (grabFirstToken(command, commandName, command)) { - if (commandName.compare("def") == 0) { - return processStepDef(block, grabbed, command); - } else if (commandName.compare("if") == 0) { - return processStepCommandIf(block, grabbed, command); - } else if (commandName.compare("endif") == 0) { - return processStepCommandEndIf(block, grabbed, command); - } else if (commandName.compare("else") == 0) { - return processStepCommandElse(block, grabbed, command); - } else if (commandName.compare("elif") == 0) { - return processStepCommandElif(block, grabbed, command); - } else if (commandName.compare("include") == 0) { - return processStepInclude(block, grabbed, command); - } else if (commandName.compare("func") == 0) { - return processStepFunc(block, grabbed, command); - } else if (commandName.compare("endfunc") == 0) { - return processStepEndFunc(block, grabbed, command); - } - } - - return block; -} - -const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) { - // then look at the define - String varName; - String val; - - if (!grabFirstToken(def, varName, val)) { - logError(block, "Invalid Var name in a tag"); - return block; - } - - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::DEF; - newBlock->command.arguments.push_back(varName); - if (!val.empty()) { - // loose first character which should be a white space - val = val.substr(val.find_first_not_of(' ')); - convertExpressionToDefArguments(val, newBlock->command.arguments); - } - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; -} - - -const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock depth - BlockPointer newIfBlock = std::make_shared(_root->sourceName); - newIfBlock->command.type = Command::IFBLOCK; - - Block::addNewBlock(parent, newIfBlock); - - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::IF; - convertExpressionToArguments(expression, newBlock->command.arguments); - - Block::addNewBlock(newIfBlock, newBlock); - - // dive in the new If branch - return newBlock; -} - -const BlockPointer TextTemplate::processStepCommandEndIf(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a if block ? - if ((parent->command.type == Command::IF) - || (parent->command.type == Command::ELIF) - || (parent->command.type == Command::ELSE)) { - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::ENDIF; - newBlock->command.arguments.push_back(expression); - - Block::addNewBlock(parent->parent->parent, newBlock); - - return newBlock; - } else { - logError(block, "Invalid block, not in a block"); - return block; - } - - return block; -} - -const BlockPointer TextTemplate::processStepCommandElse(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a if block ? - if ((parent->command.type == Command::IF) - || (parent->command.type == Command::ELIF)) { - // All good go back to the IfBlock - parent = parent->parent; - - // Add a new BLock depth - BlockPointer newBlock = std::make_shared(_root->sourceName); - newBlock->ostr << grabbed; - newBlock->command.type = Command::ELSE; - newBlock->command.arguments.push_back(expression); - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; - - } else if ((block)->command.type == Command::ELSE) { - logError(block, "Invalid block, in a block but after a block"); - return block; - } else { - logError(block, "Invalid block, not in a block"); - return block; - } -} - -const BlockPointer TextTemplate::processStepCommandElif(const BlockPointer& block, String& grabbed, String& expression) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a if block ? - if ((parent->command.type == Command::IF) - || (parent->command.type == Command::ELIF)) { - // All good go back to the IfBlock - parent = parent->parent; - - // Add a new BLock depth - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::ELIF; - convertExpressionToArguments(expression, newBlock->command.arguments); - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; - - } else if (parent->command.type == Command::ELSE) { - logError(block, "Invalid block, in a block but after a block"); - return block; - } else { - logError(block, "Invalid block, not in a block"); - return block; - } -} - -const BlockPointer TextTemplate::processStepRemark(const BlockPointer& block, String& grabbed, String& tag) { - // nothing to do :) - - // no need to think, let's just add the grabbed text - (block->ostr) << grabbed; - - return block; -} - -const BlockPointer TextTemplate::processStepInclude(const BlockPointer& block, String& grabbed, String& include) { - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::INCLUDE; - convertExpressionToArguments(include, newBlock->command.arguments); - - Block::addNewBlock(parent, newBlock); - - TextTemplatePointer inc = Config::addInclude(_config, newBlock->command.arguments.front().c_str()); - if (!inc) { - logError(newBlock, "Failed to include %s", newBlock->command.arguments.front().c_str()); - } - - // dive in the new block - return newBlock; -} - -const BlockPointer TextTemplate::processStepFunc(const BlockPointer& block, String& grabbed, String& func) { - // then look at the define - String varName; - String var; - - if (!grabFirstToken(func, varName, var)) { - logError(block, "Invalid func name <%s> in a block", func.c_str()); - return block; - } - - // DOes the func already exists ? - if (_config->_funcs.findFunc(varName.c_str())) { - logError(block, "Declaring a new func named <%s> already exists", func.c_str()); - return block; - } - - BlockPointer parent = Block::getCurrentBlock(block); - - // Add a new BLock - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::FUNC; - newBlock->command.arguments.push_back(varName); - convertExpressionToFuncArguments(var, newBlock->command.arguments); - - _config->_funcs.addFunc(varName.c_str(), newBlock); - - Block::addNewBlock(parent, newBlock); - - // dive in the new block - return newBlock; -} - -const BlockPointer TextTemplate::processStepEndFunc(const BlockPointer& block, String& grabbed, String& tag) { - BlockPointer parent = Block::getCurrentBlock(block); - - // are we in a func block ? - if (parent->command.type == Command::FUNC) { - // Make sure the FUnc has been declared properly - BlockPointer funcBlock = _config->_funcs.findFunc(parent->command.arguments.front().c_str()); - if (funcBlock != parent) { - logError(block, "Mismatching blocks"); - return BlockPointer(); - } - - // Everything is cool , so let's unplugg the FUnc block from this tree and just put the EndFunc block - - BlockPointer newBlock = std::make_shared(_root->sourceName); - (newBlock->ostr) << grabbed; - - newBlock->command.type = Command::ENDFUNC; - convertExpressionToArguments(tag, newBlock->command.arguments); - - newBlock->parent = parent->parent; - - parent->parent->blocks.back() = newBlock; - - parent->parent = 0; - - // dive in the new block - return newBlock; - } else { - logError(block, "Invalid block, not in a block"); - return BlockPointer(); - } - - return block; -} - -TextTemplate::TextTemplate(const String& name, const ConfigPointer& externalConfig) : - _config(externalConfig), - _root(new Block(name)), - _numErrors(0), - _steppingStarted(false) { -} - -TextTemplate::~TextTemplate() { -} - -int TextTemplate::scribe(std::ostream& dst, std::istream& src, Vars& vars) { - int nbErrors = parse(src); - - if (nbErrors == 0) { - return generate(dst, vars); - } - return nbErrors; -} - -int TextTemplate::generateTree(std::ostream& dst, const BlockPointer& block, Vars& vars) { - BlockPointer newCurrentBlock; - int numPasses = evalBlockGeneration(dst, block, vars, newCurrentBlock); - for (int passNum= 0; passNum < numPasses; passNum++) { - dst << newCurrentBlock->ostr.str(); - - for (auto child : newCurrentBlock->blocks) { - generateTree(dst, child, vars); - } - } - - return _numErrors; -} - -int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& block, Vars& vars, BlockPointer& branch) { - switch (block->command.type) { - case Command::BLOCK: { - branch = block; - return 1; - } - break; - case Command::VAR: { - Vars::iterator it = vars.find(block->command.arguments.front()); - if (it != vars.end()) { - dst << (*it).second; - } else { - BlockPointer funcBlock = _config->_funcs.findFunc(block->command.arguments.front().c_str()); - if (funcBlock) { - // before diving in the func tree, let's modify the vars with the local defs: - int nbParams = std::min(block->command.arguments.size(), funcBlock->command.arguments.size()); - std::vector< String > paramCache; - paramCache.push_back(""); - String val; - for (int i = 1; i < nbParams; i++) { - val = block->command.arguments[i]; - if ((val[0] == Tag::VAR) && (val[val.length()-1] == Tag::VAR)) { - val = val.substr(1, val.length()-2); - Vars::iterator it = vars.find(val); - if (it != vars.end()) { - val = (*it).second; - } - } - - Vars::iterator it = vars.find(funcBlock->command.arguments[i]); - if (it != vars.end()) { - paramCache.push_back((*it).second); - (*it).second = val; - } else { - vars.insert(Vars::value_type(funcBlock->command.arguments[i], val)); - paramCache.push_back(""); - } - } - - generateTree(dst, funcBlock, vars); - - for (int i = 1; i < nbParams; i++) { - vars[ funcBlock->command.arguments[i] ] = paramCache[i]; - } - } - } - branch = block; - return 1; - } - break; - case Command::IFBLOCK: { - // ok, go through the branches and pick the first one that goes - for (auto child: block->blocks) { - int numPasses = evalBlockGeneration(dst, child, vars, branch); - if (numPasses > 0) { - return numPasses; - } - } - } - break; - case Command::IF: - case Command::ELIF: { - if (!block->command.arguments.empty()) { - // Just one argument means check for the var beeing defined - if (block->command.arguments.size() == 1) { - Vars::iterator it = vars.find(block->command.arguments.front()); - if (it != vars.end()) { - branch = block; - return 1; - } - } else if (block->command.arguments.size() == 2) { - if (block->command.arguments[0].compare("not") == 0) { - Vars::iterator it = vars.find(block->command.arguments[1]); - if (it == vars.end()) { - branch = block; - return 1; - } - } - } else if (block->command.arguments.size() == 3) { - if (block->command.arguments[1].compare("and") == 0) { - Vars::iterator itL = vars.find(block->command.arguments[0]); - Vars::iterator itR = vars.find(block->command.arguments[2]); - if ((itL != vars.end()) && (itR != vars.end())) { - branch = block; - return 1; - } - } else if (block->command.arguments[1].compare("or") == 0) { - Vars::iterator itL = vars.find(block->command.arguments[0]); - Vars::iterator itR = vars.find(block->command.arguments[2]); - if ((itL != vars.end()) || (itR != vars.end())) { - branch = block; - return 1; - } - } else if (block->command.arguments[1].compare("==") == 0) { - Vars::iterator itL = vars.find(block->command.arguments[0]); - if (itL != vars.end()) { - if ((*itL).second.compare(block->command.arguments[2]) == 0) { - branch = block; - return 1; - } - } - } - } - - } - return 0; - } - break; - case Command::ELSE: { - branch = block; - return 1; - } - break; - case Command::ENDIF: { - branch = block; - return 1; - } - break; - case Command::DEF: { - if (block->command.arguments.size()) { - // THe actual value of the var defined sneeds to be evaluated: - String val; - for (unsigned int t = 1; t < block->command.arguments.size(); t++) { - // detect if a param is a var - int len = block->command.arguments[t].length(); - if ((block->command.arguments[t][0] == Tag::VAR) - && (block->command.arguments[t][len - 1] == Tag::VAR)) { - String var = block->command.arguments[t].substr(1, len - 2); - Vars::iterator it = vars.find(var); - if (it != vars.end()) { - val += (*it).second; - } - } else { - val += block->command.arguments[t]; - } - } - - Vars::iterator it = vars.find(block->command.arguments.front()); - if (it == vars.end()) { - vars.insert(Vars::value_type(block->command.arguments.front(), val)); - } else { - (*it).second = val; - } - - branch = block; - return 1; - } else { - branch = block; - return 0; - } - } - break; - - case Command::INCLUDE: { - TextTemplatePointer include = _config->findInclude(block->command.arguments.front().c_str()); - if (include && !include->_root->blocks.empty()) { - if (include->_root) { - generateTree(dst, include->_root, vars); - } - } - - branch = block; - return 1; - } - break; - - case Command::FUNC: { - branch = block; - return 1; - } - break; - - case Command::ENDFUNC: { - branch = block; - return 1; - } - break; - - default: { - } - } - - return 0; -} - - -int TextTemplate::parse(std::istream& src) { - _root->command.type = Command::BLOCK; - BlockPointer currentBlock = _root; - _numErrors = 0; - - // First Parse the input file - int nbLoops = 0; - bool goOn = true; - Tag::Type tagType = Tag::INVALID; - Tag::Type nextTagType = Tag::INVALID; - while (goOn) { - std::string data; - std::string tag; - if (stepForward(&src, data, tag, tagType, nextTagType)) { - currentBlock = processStep(currentBlock, data, tag, tagType); - } else { - goOn = false; - } - - tagType = nextTagType; - nbLoops++; - } - - return _numErrors; -} - -int TextTemplate::generate(std::ostream& dst, Vars& vars) { - return generateTree(dst, _root, vars); -} - -void TextTemplate::displayTree(std::ostream& dst, int& level) const { - Block::displayTree(_root, dst, level); -} - -void TextTemplate::Block::displayTree(const BlockPointer& block, std::ostream& dst, int& level) { - String tab(level * 2, ' '); - - const String BLOCK_TYPE_NAMES[] = { - "VAR", - "BLOCK", - "FUNC", - "ENDFUNC", - "IFBLOCK", - "IF", - "ELIF", - "ELSE", - "ENDIF", - "FOR", - "ENDFOR", - "INCLUDE", - "DEF" - }; - - dst << tab << "{ " << BLOCK_TYPE_NAMES[block->command.type] << ":"; - if (!block->command.arguments.empty()) { - for (auto arg: block->command.arguments) { - dst << " " << arg; - } - } - dst << std::endl; - - level++; - for (auto sub: block->blocks) { - displayTree(sub, dst, level); - } - level--; - - dst << tab << "}" << std::endl; -} - -void TextTemplate::Config::displayTree(std::ostream& dst, int& level) const { - String tab(level * 2, ' '); - - level++; - dst << tab << "Includes:" << std::endl; - for (auto inc: _includes) { - dst << tab << tab << inc.first << std::endl; - inc.second->displayTree(dst, level); - } - dst << tab << "Funcs:" << std::endl; - for (auto func: _funcs._funcs) { - dst << tab << tab << func.first << std::endl; - TextTemplate::Block::displayTree( func.second, dst, level); - } - level--; -} +// +// TextTemplate.cpp +// tools/shaderScribe/src +// +// Created by Sam Gateau on 12/15/2014. +// 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 +#include "TextTemplate.h" + +#include +#include +#include +#include + +typedef TextTemplate::Block::Pointer BlockPointer; +typedef TextTemplate::Config::Pointer ConfigPointer; +typedef TextTemplate::Pointer TextTemplatePointer; + +//----------------------------------------------------------------------------- +TextTemplate::Config::Config() : + _includes(), + _funcs(), + _logStream(&std::cout), + _numErrors(0), + _includerCallback(TextTemplate::loadFile) { + _paths.push_back(""); +} + +const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) { + if (!config) { + return TextTemplatePointer(); + } + + TextTemplatePointer included = config->findInclude(include); + if (included) { + return included; + } + + // INcluded doest exist yet so let's try to create it + String includeStream; + if (config->_includerCallback(config, include, includeStream)) { + // ok, then create a new Template on the include file with this as lib + included = std::make_shared(include, config); + + std::stringstream src(includeStream); + + int nbErrors = included->parse(src); + if (nbErrors > 0) { + included->logError(included->_root, "File failed to parse, not included"); + return TextTemplatePointer(); + } + config->_includes.insert(Includes::value_type(include, included)); + return included; + } + + return TextTemplatePointer(); +} + +const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) { + Includes::iterator includeIt = _includes.find(String(include)); + if (includeIt != _includes.end()) { + return (*includeIt).second; + } else { + return TextTemplatePointer(); + } +} + +void TextTemplate::Config::addIncludePath(const char* path) { + _paths.push_back(String(path)); +} + +bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) { + String sourceFile(filename); + String fullfilename; + for (unsigned int i = 0; i < config->_paths.size(); i++) { + fullfilename = config->_paths[i] + sourceFile; + std::ifstream ifs; + ifs.open(fullfilename.c_str()); + if (ifs.is_open()) { + std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + source = str; + ifs.close(); + return (source.length() > 0); + } + } + + return false; +} + +TextTemplate::Funcs::Funcs() : + _funcs() { +} + +TextTemplate::Funcs::~Funcs() { +} + + +const BlockPointer TextTemplate::Funcs::findFunc(const char* func) { + map::iterator it = _funcs.find(String(func)); + if (it != _funcs.end()) { + return (*it).second; + } else { + return BlockPointer(); + } +} + +const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) { + BlockPointer included = findFunc(func); + if (! included) { + _funcs.insert(map::value_type(func, funcBlock)); + } + return included; +} + +void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) { + if (parent) { + parent->blocks.push_back(block); + block->parent = parent; + } +} + +const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) { + if (block && block->command.isBlockEnd()) { + return block->parent; + } else { + return block; + } +} + +void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) { + va_list argp; + va_start(argp, fmt); + + char buff[256]; + sprintf(buff, fmt, argp); + + _numErrors++; + log() << block->sourceName << " Error >>" << buff << std::endl; + + int level = 1; + displayTree(std::cerr, level); + +} + +bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { + std::stringstream dst; + while (!str->eof()) { + + std::string datatoken; + getline((*str), datatoken, Tag::BEGIN); + dst << datatoken; + + char next = str->peek(); + if (next == Tag::VAR) { + tagType = Tag::VARIABLE; + grabbed = dst.str(); + str->get(); // skip tag char + return true; + } else if (next == Tag::COM) { + tagType = Tag::COMMAND; + grabbed = dst.str(); + str->get(); // skip tag char + return true; + } else if (next == Tag::REM) { + tagType = Tag::REMARK; + grabbed = dst.str(); + str->get(); // skip tag char + return true; + } else { + if (!str->eof()) { + // false positive, just found the Tag::BEGIN without consequence + dst << Tag::BEGIN; + // keep searching + } else { + // end of the file finishing with no tag + tagType = Tag::INVALID; + grabbed = dst.str(); + return true; + } + } + } + + return false; +} + +bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { + std::stringstream dst; + + // preEnd char depends on tag type + char preEnd = Tag::COM; + if (tagType == Tag::VARIABLE) { + preEnd = Tag::VAR; + } else if (tagType == Tag::REMARK) { + preEnd = Tag::REM; + } + + while (!str->eof() && !str->fail()) { + // looking for the end of the tag means find the next preEnd + std::string dataToken; + getline((*str), dataToken, preEnd); + char end = str->peek(); + + dst << dataToken; + + // and if the next char is Tag::END then that's it + if (end == Tag::END) { + grabbed = dst.str(); + str->get(); // eat the Tag::END + return true; + } else { + // false positive, keep on searching + dst << preEnd; + } + } + grabbed = dst.str(); + return false; +} + +bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType, + Tag::Type& nextTagType) { + if (str->eof()) { + return false; + } + + if (!_steppingStarted) { + _steppingStarted = true; + return grabUntilBeginTag(str, grabbed, nextTagType); + } + + // Read from the last opening Tag::BEGIN captured to the next Tag::END + if (grabUntilEndTag(str, tag, tagType)) { + // skip trailing space and new lines only after Command or Remark tag block + if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) { + while (!str->eof() && !str->fail()) { + char c = str->peek(); + if ((c == ' ') || (c == '\t') || (c == '\n')) { + str->get(); + } else { + break; + } + } + } + + grabUntilBeginTag(str, grabbed, nextTagType); + } + + return true; //hasElement; +} + +const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag, + Tag::Type& tagType) { + switch (tagType) { + case Tag::INVALID: + block->ostr << grabbed; + return block; + break; + case Tag::VARIABLE: + return processStepVar(block, grabbed, tag); + break; + case Tag::COMMAND: + return processStepCommand(block, grabbed, tag); + break; + case Tag::REMARK: + return processStepRemark(block, grabbed, tag); + break; + } + + logError(block, "Invalid tag"); + return block; +} + +bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) { + bool goOn = true; + std::string::size_type i = 0; + while (goOn && (i < src.length())) { + char c = src[i]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) { + token += c; + } else { + if (!token.empty()) { + reminder = src.substr(i); + return true; + } + } + i++; + } + + return (!token.empty()); +} + +bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) { + std::stringstream str(src); + String token; + + while (!str.eof()) { + str >> token; + if (!str.fail()) { + arguments.push_back(token); + } + } + + return true; +} + +bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) { + if (src.empty()) { + return false; + } + + std::stringstream argstr(src); + std::stringstream dest; + bool inVar = false; + while (!argstr.eof()) { + // parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END + String token; + char tag; + if (!inVar) { + getline(argstr, token, Tag::BEGIN); + dest << token; + tag = argstr.peek(); + } else { + getline(argstr, token, Tag::END); + dest << token; + tag = token.back(); + } + + if (tag == Tag::VAR) { + if (!inVar) { + // real var coming: + arguments.push_back(dest.str()); + inVar = true; + } else { + // real var over + arguments.push_back(dest.str()); + inVar = false; + } + } else { + if (argstr.eof()) { + arguments.push_back(dest.str()); + } else { + // put back the tag char stolen + dest << (!inVar ? Tag::BEGIN : Tag::END); + } + } + } + + return true; +} + +bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) { + if (src.empty()) { + return false; + } + std::stringstream streamSrc(src); + String params; + getline(streamSrc, params, '('); + getline(streamSrc, params, ')'); + if (params.empty()) { + return false; + } + + std::stringstream str(params); + String token; + int nbTokens = 0; + while (!str.eof()) { + char c = str.peek(); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) { + token += c; + } else if (c == ',') { + if (!token.empty()) { + arguments.push_back(token); + nbTokens++; + } + token.clear(); + } + + str.get(); + } + + if (token.size()) { + arguments.push_back(token); + nbTokens++; + } + + return (nbTokens > 0); +} + +const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) { + // then look at the define + String var = tag; + String varName; + String val; + + if (grabFirstToken(var, varName, val)) { + if (!varName.empty()) { + BlockPointer parent = Block::getCurrentBlock(block); + + // Add a new BLock + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::VAR; + + newBlock->command.arguments.push_back(varName); + + if (!val.empty()) { + convertExpressionToFuncArguments(val, newBlock->command.arguments); + } + + Block::addNewBlock(parent, newBlock); + + // dive in the new block + return newBlock; + } + } + + return block; +} + +const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) { + // Grab the command name + String command = tag; + String commandName; + + if (grabFirstToken(command, commandName, command)) { + if (commandName.compare("def") == 0) { + return processStepDef(block, grabbed, command); + } else if (commandName.compare("if") == 0) { + return processStepCommandIf(block, grabbed, command); + } else if (commandName.compare("endif") == 0) { + return processStepCommandEndIf(block, grabbed, command); + } else if (commandName.compare("else") == 0) { + return processStepCommandElse(block, grabbed, command); + } else if (commandName.compare("elif") == 0) { + return processStepCommandElif(block, grabbed, command); + } else if (commandName.compare("include") == 0) { + return processStepInclude(block, grabbed, command); + } else if (commandName.compare("func") == 0) { + return processStepFunc(block, grabbed, command); + } else if (commandName.compare("endfunc") == 0) { + return processStepEndFunc(block, grabbed, command); + } + } + + return block; +} + +const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) { + // then look at the define + String varName; + String val; + + if (!grabFirstToken(def, varName, val)) { + logError(block, "Invalid Var name in a tag"); + return block; + } + + BlockPointer parent = Block::getCurrentBlock(block); + + // Add a new BLock + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::DEF; + newBlock->command.arguments.push_back(varName); + if (!val.empty()) { + // loose first character which should be a white space + val = val.substr(val.find_first_not_of(' ')); + convertExpressionToDefArguments(val, newBlock->command.arguments); + } + + Block::addNewBlock(parent, newBlock); + + // dive in the new block + return newBlock; +} + + +const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) { + BlockPointer parent = Block::getCurrentBlock(block); + + // Add a new BLock depth + BlockPointer newIfBlock = std::make_shared(_root->sourceName); + newIfBlock->command.type = Command::IFBLOCK; + + Block::addNewBlock(parent, newIfBlock); + + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::IF; + convertExpressionToArguments(expression, newBlock->command.arguments); + + Block::addNewBlock(newIfBlock, newBlock); + + // dive in the new If branch + return newBlock; +} + +const BlockPointer TextTemplate::processStepCommandEndIf(const BlockPointer& block, String& grabbed, String& expression) { + BlockPointer parent = Block::getCurrentBlock(block); + + // are we in a if block ? + if ((parent->command.type == Command::IF) + || (parent->command.type == Command::ELIF) + || (parent->command.type == Command::ELSE)) { + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::ENDIF; + newBlock->command.arguments.push_back(expression); + + Block::addNewBlock(parent->parent->parent, newBlock); + + return newBlock; + } else { + logError(block, "Invalid block, not in a block"); + return block; + } + + return block; +} + +const BlockPointer TextTemplate::processStepCommandElse(const BlockPointer& block, String& grabbed, String& expression) { + BlockPointer parent = Block::getCurrentBlock(block); + + // are we in a if block ? + if ((parent->command.type == Command::IF) + || (parent->command.type == Command::ELIF)) { + // All good go back to the IfBlock + parent = parent->parent; + + // Add a new BLock depth + BlockPointer newBlock = std::make_shared(_root->sourceName); + newBlock->ostr << grabbed; + newBlock->command.type = Command::ELSE; + newBlock->command.arguments.push_back(expression); + + Block::addNewBlock(parent, newBlock); + + // dive in the new block + return newBlock; + + } else if ((block)->command.type == Command::ELSE) { + logError(block, "Invalid block, in a block but after a block"); + return block; + } else { + logError(block, "Invalid block, not in a block"); + return block; + } +} + +const BlockPointer TextTemplate::processStepCommandElif(const BlockPointer& block, String& grabbed, String& expression) { + BlockPointer parent = Block::getCurrentBlock(block); + + // are we in a if block ? + if ((parent->command.type == Command::IF) + || (parent->command.type == Command::ELIF)) { + // All good go back to the IfBlock + parent = parent->parent; + + // Add a new BLock depth + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::ELIF; + convertExpressionToArguments(expression, newBlock->command.arguments); + + Block::addNewBlock(parent, newBlock); + + // dive in the new block + return newBlock; + + } else if (parent->command.type == Command::ELSE) { + logError(block, "Invalid block, in a block but after a block"); + return block; + } else { + logError(block, "Invalid block, not in a block"); + return block; + } +} + +const BlockPointer TextTemplate::processStepRemark(const BlockPointer& block, String& grabbed, String& tag) { + // nothing to do :) + + // no need to think, let's just add the grabbed text + (block->ostr) << grabbed; + + return block; +} + +const BlockPointer TextTemplate::processStepInclude(const BlockPointer& block, String& grabbed, String& include) { + BlockPointer parent = Block::getCurrentBlock(block); + + // Add a new BLock + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::INCLUDE; + convertExpressionToArguments(include, newBlock->command.arguments); + + Block::addNewBlock(parent, newBlock); + + TextTemplatePointer inc = Config::addInclude(_config, newBlock->command.arguments.front().c_str()); + if (!inc) { + logError(newBlock, "Failed to include %s", newBlock->command.arguments.front().c_str()); + } + + // dive in the new block + return newBlock; +} + +const BlockPointer TextTemplate::processStepFunc(const BlockPointer& block, String& grabbed, String& func) { + // then look at the define + String varName; + String var; + + if (!grabFirstToken(func, varName, var)) { + logError(block, "Invalid func name <%s> in a block", func.c_str()); + return block; + } + + // DOes the func already exists ? + if (_config->_funcs.findFunc(varName.c_str())) { + logError(block, "Declaring a new func named <%s> already exists", func.c_str()); + return block; + } + + BlockPointer parent = Block::getCurrentBlock(block); + + // Add a new BLock + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::FUNC; + newBlock->command.arguments.push_back(varName); + convertExpressionToFuncArguments(var, newBlock->command.arguments); + + _config->_funcs.addFunc(varName.c_str(), newBlock); + + Block::addNewBlock(parent, newBlock); + + // dive in the new block + return newBlock; +} + +const BlockPointer TextTemplate::processStepEndFunc(const BlockPointer& block, String& grabbed, String& tag) { + BlockPointer parent = Block::getCurrentBlock(block); + + // are we in a func block ? + if (parent->command.type == Command::FUNC) { + // Make sure the FUnc has been declared properly + BlockPointer funcBlock = _config->_funcs.findFunc(parent->command.arguments.front().c_str()); + if (funcBlock != parent) { + logError(block, "Mismatching blocks"); + return BlockPointer(); + } + + // Everything is cool , so let's unplugg the FUnc block from this tree and just put the EndFunc block + + BlockPointer newBlock = std::make_shared(_root->sourceName); + (newBlock->ostr) << grabbed; + + newBlock->command.type = Command::ENDFUNC; + convertExpressionToArguments(tag, newBlock->command.arguments); + + newBlock->parent = parent->parent; + + parent->parent->blocks.back() = newBlock; + + parent->parent = 0; + + // dive in the new block + return newBlock; + } else { + logError(block, "Invalid block, not in a block"); + return BlockPointer(); + } + + return block; +} + +TextTemplate::TextTemplate(const String& name, const ConfigPointer& externalConfig) : + _config(externalConfig), + _root(new Block(name)), + _numErrors(0), + _steppingStarted(false) { +} + +TextTemplate::~TextTemplate() { +} + +int TextTemplate::scribe(std::ostream& dst, std::istream& src, Vars& vars) { + int nbErrors = parse(src); + + if (nbErrors == 0) { + return generate(dst, vars); + } + return nbErrors; +} + +int TextTemplate::generateTree(std::ostream& dst, const BlockPointer& block, Vars& vars) { + BlockPointer newCurrentBlock; + int numPasses = evalBlockGeneration(dst, block, vars, newCurrentBlock); + for (int passNum= 0; passNum < numPasses; passNum++) { + dst << newCurrentBlock->ostr.str(); + + for (auto child : newCurrentBlock->blocks) { + generateTree(dst, child, vars); + } + } + + return _numErrors; +} + +int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& block, Vars& vars, BlockPointer& branch) { + switch (block->command.type) { + case Command::BLOCK: { + branch = block; + return 1; + } + break; + case Command::VAR: { + Vars::iterator it = vars.find(block->command.arguments.front()); + if (it != vars.end()) { + dst << (*it).second; + } else { + BlockPointer funcBlock = _config->_funcs.findFunc(block->command.arguments.front().c_str()); + if (funcBlock) { + // before diving in the func tree, let's modify the vars with the local defs: + int nbParams = std::min(block->command.arguments.size(), funcBlock->command.arguments.size()); + std::vector< String > paramCache; + paramCache.push_back(""); + String val; + for (int i = 1; i < nbParams; i++) { + val = block->command.arguments[i]; + if ((val[0] == Tag::VAR) && (val[val.length()-1] == Tag::VAR)) { + val = val.substr(1, val.length()-2); + Vars::iterator it = vars.find(val); + if (it != vars.end()) { + val = (*it).second; + } + } + + Vars::iterator it = vars.find(funcBlock->command.arguments[i]); + if (it != vars.end()) { + paramCache.push_back((*it).second); + (*it).second = val; + } else { + vars.insert(Vars::value_type(funcBlock->command.arguments[i], val)); + paramCache.push_back(""); + } + } + + generateTree(dst, funcBlock, vars); + + for (int i = 1; i < nbParams; i++) { + vars[ funcBlock->command.arguments[i] ] = paramCache[i]; + } + } + } + branch = block; + return 1; + } + break; + case Command::IFBLOCK: { + // ok, go through the branches and pick the first one that goes + for (auto child: block->blocks) { + int numPasses = evalBlockGeneration(dst, child, vars, branch); + if (numPasses > 0) { + return numPasses; + } + } + } + break; + case Command::IF: + case Command::ELIF: { + if (!block->command.arguments.empty()) { + // Just one argument means check for the var beeing defined + if (block->command.arguments.size() == 1) { + Vars::iterator it = vars.find(block->command.arguments.front()); + if (it != vars.end()) { + branch = block; + return 1; + } + } else if (block->command.arguments.size() == 2) { + if (block->command.arguments[0].compare("not") == 0) { + Vars::iterator it = vars.find(block->command.arguments[1]); + if (it == vars.end()) { + branch = block; + return 1; + } + } + } else if (block->command.arguments.size() == 3) { + if (block->command.arguments[1].compare("and") == 0) { + Vars::iterator itL = vars.find(block->command.arguments[0]); + Vars::iterator itR = vars.find(block->command.arguments[2]); + if ((itL != vars.end()) && (itR != vars.end())) { + branch = block; + return 1; + } + } else if (block->command.arguments[1].compare("or") == 0) { + Vars::iterator itL = vars.find(block->command.arguments[0]); + Vars::iterator itR = vars.find(block->command.arguments[2]); + if ((itL != vars.end()) || (itR != vars.end())) { + branch = block; + return 1; + } + } else if (block->command.arguments[1].compare("==") == 0) { + Vars::iterator itL = vars.find(block->command.arguments[0]); + if (itL != vars.end()) { + if ((*itL).second.compare(block->command.arguments[2]) == 0) { + branch = block; + return 1; + } + } + } + } + + } + return 0; + } + break; + case Command::ELSE: { + branch = block; + return 1; + } + break; + case Command::ENDIF: { + branch = block; + return 1; + } + break; + case Command::DEF: { + if (block->command.arguments.size()) { + // THe actual value of the var defined sneeds to be evaluated: + String val; + for (unsigned int t = 1; t < block->command.arguments.size(); t++) { + // detect if a param is a var + int len = block->command.arguments[t].length(); + if ((block->command.arguments[t][0] == Tag::VAR) + && (block->command.arguments[t][len - 1] == Tag::VAR)) { + String var = block->command.arguments[t].substr(1, len - 2); + Vars::iterator it = vars.find(var); + if (it != vars.end()) { + val += (*it).second; + } + } else { + val += block->command.arguments[t]; + } + } + + Vars::iterator it = vars.find(block->command.arguments.front()); + if (it == vars.end()) { + vars.insert(Vars::value_type(block->command.arguments.front(), val)); + } else { + (*it).second = val; + } + + branch = block; + return 1; + } else { + branch = block; + return 0; + } + } + break; + + case Command::INCLUDE: { + TextTemplatePointer include = _config->findInclude(block->command.arguments.front().c_str()); + if (include && !include->_root->blocks.empty()) { + if (include->_root) { + generateTree(dst, include->_root, vars); + } + } + + branch = block; + return 1; + } + break; + + case Command::FUNC: { + branch = block; + return 1; + } + break; + + case Command::ENDFUNC: { + branch = block; + return 1; + } + break; + + default: { + } + } + + return 0; +} + + +int TextTemplate::parse(std::istream& src) { + _root->command.type = Command::BLOCK; + BlockPointer currentBlock = _root; + _numErrors = 0; + + // First Parse the input file + int nbLoops = 0; + bool goOn = true; + Tag::Type tagType = Tag::INVALID; + Tag::Type nextTagType = Tag::INVALID; + while (goOn) { + std::string data; + std::string tag; + if (stepForward(&src, data, tag, tagType, nextTagType)) { + currentBlock = processStep(currentBlock, data, tag, tagType); + } else { + goOn = false; + } + + tagType = nextTagType; + nbLoops++; + } + + return _numErrors; +} + +int TextTemplate::generate(std::ostream& dst, Vars& vars) { + return generateTree(dst, _root, vars); +} + +void TextTemplate::displayTree(std::ostream& dst, int& level) const { + Block::displayTree(_root, dst, level); +} + +void TextTemplate::Block::displayTree(const BlockPointer& block, std::ostream& dst, int& level) { + String tab(level * 2, ' '); + + const String BLOCK_TYPE_NAMES[] = { + "VAR", + "BLOCK", + "FUNC", + "ENDFUNC", + "IFBLOCK", + "IF", + "ELIF", + "ELSE", + "ENDIF", + "FOR", + "ENDFOR", + "INCLUDE", + "DEF" + }; + + dst << tab << "{ " << BLOCK_TYPE_NAMES[block->command.type] << ":"; + if (!block->command.arguments.empty()) { + for (auto arg: block->command.arguments) { + dst << " " << arg; + } + } + dst << std::endl; + + level++; + for (auto sub: block->blocks) { + displayTree(sub, dst, level); + } + level--; + + dst << tab << "}" << std::endl; +} + +void TextTemplate::Config::displayTree(std::ostream& dst, int& level) const { + String tab(level * 2, ' '); + + level++; + dst << tab << "Includes:" << std::endl; + for (auto inc: _includes) { + dst << tab << tab << inc.first << std::endl; + inc.second->displayTree(dst, level); + } + dst << tab << "Funcs:" << std::endl; + for (auto func: _funcs._funcs) { + dst << tab << tab << func.first << std::endl; + TextTemplate::Block::displayTree( func.second, dst, level); + } + level--; +} diff --git a/tools/scribe/src/TextTemplate.h b/tools/scribe/src/TextTemplate.h index a19d2938da..a6fd04da5c 100755 --- a/tools/scribe/src/TextTemplate.h +++ b/tools/scribe/src/TextTemplate.h @@ -1,4 +1,4 @@ - + // // TextTemplate.cpp // tools/shaderScribe/src @@ -7,198 +7,198 @@ // 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_TEXT_TEMPLATE_H -#define hifi_TEXT_TEMPLATE_H - -#include -#include -#include -#include -#include -#include -#include - -class TextTemplate { -public: - typedef std::shared_ptr< TextTemplate > Pointer; - typedef std::string String; - typedef std::vector< String > StringVector; - typedef std::map< String, String > Vars; - typedef std::map< String, TextTemplate::Pointer > Includes; - - class Tag { - public: - enum Type { - VARIABLE = 0, - COMMAND, - REMARK, - INVALID = -1, - }; - - static const char BEGIN = '<'; - static const char END = '>'; - - static const char VAR = '$'; - static const char COM = '@'; - static const char REM = '!'; - }; - - class Command { - public: - typedef std::vector< Command > vector; - - enum Type { - VAR = 0, - BLOCK, - FUNC, - ENDFUNC, - IFBLOCK, - IF, - ELIF, - ELSE, - ENDIF, - FOR, - ENDFOR, - INCLUDE, - DEF, - }; - - Type type; - std::vector< String > arguments; - - bool isBlockEnd() { - switch (type) { - case ENDFUNC: - case ENDIF: - case ENDFOR: - case INCLUDE: - case DEF: - case VAR: - return true; - default: - return false; - } - } - }; - - class Block { - public: - typedef std::shared_ptr Pointer; - typedef std::vector< Block::Pointer > Vector; - - Block::Pointer parent; - Command command; - Vector blocks; - std::stringstream ostr; - - String sourceName; - - Block(const String& sourceFilename) : - sourceName(sourceFilename) {} - - static void addNewBlock(const Block::Pointer& parent, const Block::Pointer& block); - static const Block::Pointer& getCurrentBlock(const Block::Pointer& block); - - static void displayTree(const Block::Pointer& block, std::ostream& dst, int& level); - }; - - class Funcs { - public: - typedef std::map< String, Block::Pointer > map; - - Funcs(); - ~Funcs(); - - const Block::Pointer findFunc(const char* func); - const Block::Pointer addFunc(const char* func, const Block::Pointer& root); - - map _funcs; - protected: - }; - - class Config { - public: - typedef std::shared_ptr< Config > Pointer; - typedef bool (*IncluderCallback) (const Config::Pointer& config, const char* filename, String& source); - - Includes _includes; - Funcs _funcs; - std::ostream* _logStream; - int _numErrors; - IncluderCallback _includerCallback; - StringVector _paths; - - Config(); - - static const TextTemplate::Pointer addInclude(const Config::Pointer& config, const char* include); - const TextTemplate::Pointer findInclude(const char* include); - - void addIncludePath(const char* path); - - void displayTree(std::ostream& dst, int& level) const; - }; - - static bool loadFile(const Config::Pointer& config, const char* filename, String& source); - - TextTemplate(const String& name, const Config::Pointer& config = std::make_shared()); - ~TextTemplate(); - - // Scibe does all the job of parsing an inout template stream and then gneerating theresulting stream using the vars - int scribe(std::ostream& dst, std::istream& src, Vars& vars); - - int parse(std::istream& src); - int generate(std::ostream& dst, Vars& vars); - - const Config::Pointer config() { return _config; } - - void displayTree(std::ostream& dst, int& level) const; - -protected: - Config::Pointer _config; - Block::Pointer _root; - int _numErrors; - bool _steppingStarted; - - bool grabUntilBeginTag(std::istream* str, String& grabbed, Tag::Type& tagType); - bool grabUntilEndTag(std::istream* str, String& grabbed, Tag::Type& tagType); - - bool stepForward(std::istream* str, String& grabbed, String& tag, Tag::Type& tagType, Tag::Type& nextTagType); - - bool grabFirstToken(String& src, String& token, String& reminder); - bool convertExpressionToArguments(String& src, std::vector< String >& arguments); - bool convertExpressionToDefArguments(String& src, std::vector< String >& arguments); - bool convertExpressionToFuncArguments(String& src, std::vector< String >& arguments); - - // Filter between var, command or comments - const Block::Pointer processStep(const Block::Pointer& block, String& grabbed, String& tag, Tag::Type& tagType); - const Block::Pointer processStepVar(const Block::Pointer& block, String& grabbed, String& tag); - const Block::Pointer processStepCommand(const Block::Pointer& block, String& grabbed, String& tag); - const Block::Pointer processStepRemark(const Block::Pointer& block, String& grabbed, String& tag); - - // Define command - const Block::Pointer processStepDef(const Block::Pointer& block, String& grabbed, String& tag); - - // If commands - const Block::Pointer processStepCommandIf(const Block::Pointer& block, String& grabbed, String& expression); - const Block::Pointer processStepCommandEndIf(const Block::Pointer& block, String& grabbed, String& expression); - const Block::Pointer processStepCommandElif(const Block::Pointer& block, String& grabbed, String& expression); - const Block::Pointer processStepCommandElse(const Block::Pointer& block, String& grabbed, String& expression); - - // Include command - const Block::Pointer processStepInclude(const Block::Pointer& block, String& grabbed, String& tag); - - // Function command - const Block::Pointer processStepFunc(const Block::Pointer& block, String& grabbed, String& tag); - const Block::Pointer processStepEndFunc(const Block::Pointer& block, String& grabbed, String& tag); - - // Generation - int generateTree(std::ostream& dst, const Block::Pointer& block, Vars& vars); - int evalBlockGeneration(std::ostream& dst, const Block::Pointer& block, Vars& vars, Block::Pointer& branch); - - // Errors - std::ostream& log() { return (* _config->_logStream); } - void logError(const Block::Pointer& block, const char* error, ...); -}; - -#endif +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +#ifndef hifi_TEXT_TEMPLATE_H +#define hifi_TEXT_TEMPLATE_H + +#include +#include +#include +#include +#include +#include +#include + +class TextTemplate { +public: + typedef std::shared_ptr< TextTemplate > Pointer; + typedef std::string String; + typedef std::vector< String > StringVector; + typedef std::map< String, String > Vars; + typedef std::map< String, TextTemplate::Pointer > Includes; + + class Tag { + public: + enum Type { + VARIABLE = 0, + COMMAND, + REMARK, + INVALID = -1, + }; + + static const char BEGIN = '<'; + static const char END = '>'; + + static const char VAR = '$'; + static const char COM = '@'; + static const char REM = '!'; + }; + + class Command { + public: + typedef std::vector< Command > vector; + + enum Type { + VAR = 0, + BLOCK, + FUNC, + ENDFUNC, + IFBLOCK, + IF, + ELIF, + ELSE, + ENDIF, + FOR, + ENDFOR, + INCLUDE, + DEF, + }; + + Type type; + std::vector< String > arguments; + + bool isBlockEnd() { + switch (type) { + case ENDFUNC: + case ENDIF: + case ENDFOR: + case INCLUDE: + case DEF: + case VAR: + return true; + default: + return false; + } + } + }; + + class Block { + public: + typedef std::shared_ptr Pointer; + typedef std::vector< Block::Pointer > Vector; + + Block::Pointer parent; + Command command; + Vector blocks; + std::stringstream ostr; + + String sourceName; + + Block(const String& sourceFilename) : + sourceName(sourceFilename) {} + + static void addNewBlock(const Block::Pointer& parent, const Block::Pointer& block); + static const Block::Pointer& getCurrentBlock(const Block::Pointer& block); + + static void displayTree(const Block::Pointer& block, std::ostream& dst, int& level); + }; + + class Funcs { + public: + typedef std::map< String, Block::Pointer > map; + + Funcs(); + ~Funcs(); + + const Block::Pointer findFunc(const char* func); + const Block::Pointer addFunc(const char* func, const Block::Pointer& root); + + map _funcs; + protected: + }; + + class Config { + public: + typedef std::shared_ptr< Config > Pointer; + typedef bool (*IncluderCallback) (const Config::Pointer& config, const char* filename, String& source); + + Includes _includes; + Funcs _funcs; + std::ostream* _logStream; + int _numErrors; + IncluderCallback _includerCallback; + StringVector _paths; + + Config(); + + static const TextTemplate::Pointer addInclude(const Config::Pointer& config, const char* include); + const TextTemplate::Pointer findInclude(const char* include); + + void addIncludePath(const char* path); + + void displayTree(std::ostream& dst, int& level) const; + }; + + static bool loadFile(const Config::Pointer& config, const char* filename, String& source); + + TextTemplate(const String& name, const Config::Pointer& config = std::make_shared()); + ~TextTemplate(); + + // Scibe does all the job of parsing an inout template stream and then gneerating theresulting stream using the vars + int scribe(std::ostream& dst, std::istream& src, Vars& vars); + + int parse(std::istream& src); + int generate(std::ostream& dst, Vars& vars); + + const Config::Pointer config() { return _config; } + + void displayTree(std::ostream& dst, int& level) const; + +protected: + Config::Pointer _config; + Block::Pointer _root; + int _numErrors; + bool _steppingStarted; + + bool grabUntilBeginTag(std::istream* str, String& grabbed, Tag::Type& tagType); + bool grabUntilEndTag(std::istream* str, String& grabbed, Tag::Type& tagType); + + bool stepForward(std::istream* str, String& grabbed, String& tag, Tag::Type& tagType, Tag::Type& nextTagType); + + bool grabFirstToken(String& src, String& token, String& reminder); + bool convertExpressionToArguments(String& src, std::vector< String >& arguments); + bool convertExpressionToDefArguments(String& src, std::vector< String >& arguments); + bool convertExpressionToFuncArguments(String& src, std::vector< String >& arguments); + + // Filter between var, command or comments + const Block::Pointer processStep(const Block::Pointer& block, String& grabbed, String& tag, Tag::Type& tagType); + const Block::Pointer processStepVar(const Block::Pointer& block, String& grabbed, String& tag); + const Block::Pointer processStepCommand(const Block::Pointer& block, String& grabbed, String& tag); + const Block::Pointer processStepRemark(const Block::Pointer& block, String& grabbed, String& tag); + + // Define command + const Block::Pointer processStepDef(const Block::Pointer& block, String& grabbed, String& tag); + + // If commands + const Block::Pointer processStepCommandIf(const Block::Pointer& block, String& grabbed, String& expression); + const Block::Pointer processStepCommandEndIf(const Block::Pointer& block, String& grabbed, String& expression); + const Block::Pointer processStepCommandElif(const Block::Pointer& block, String& grabbed, String& expression); + const Block::Pointer processStepCommandElse(const Block::Pointer& block, String& grabbed, String& expression); + + // Include command + const Block::Pointer processStepInclude(const Block::Pointer& block, String& grabbed, String& tag); + + // Function command + const Block::Pointer processStepFunc(const Block::Pointer& block, String& grabbed, String& tag); + const Block::Pointer processStepEndFunc(const Block::Pointer& block, String& grabbed, String& tag); + + // Generation + int generateTree(std::ostream& dst, const Block::Pointer& block, Vars& vars); + int evalBlockGeneration(std::ostream& dst, const Block::Pointer& block, Vars& vars, Block::Pointer& branch); + + // Errors + std::ostream& log() { return (* _config->_logStream); } + void logError(const Block::Pointer& block, const char* error, ...); +}; + +#endif From bddb0e1e7b279995b5dca2dba3ac42dbe5c9519c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 09:43:08 -0700 Subject: [PATCH 0216/1003] ifdef out buggy texture map code --- libraries/model/src/model/TextureMap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 584debafb2..d443522a6f 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -186,6 +186,8 @@ double mapComponent(double sobelValue) { gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; + + #if 0 // PR 5540 by AlessandroSigna // integrated here as a specialized TextureLoader for bumpmaps // The conversion is done using the Sobel Filter to calculate the derivatives from the grayscale image @@ -236,6 +238,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm result.setPixel(i, j, qRgbValue); } } + #endif gpu::Texture* theTexture = nullptr; if ((image.width() > 0) && (image.height() > 0)) { From 742e48b0c8df9aac19b133b9c9607f93a29f064e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 09:48:34 -0700 Subject: [PATCH 0217/1003] try another way of fixing held object snagging at slow velocity --- .../src/RenderableModelEntityItem.cpp | 7 ++++--- libraries/physics/src/EntityMotionState.cpp | 15 +++++++++++++++ libraries/physics/src/ObjectMotionState.cpp | 10 +--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bfed81a944..e028ee89ea 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -299,9 +299,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { bool movingOrAnimating = isMoving() || isAnimatingSomething(); if ((movingOrAnimating || - _needsInitialSimulation || - _model->getTranslation() != getPosition() || - _model->getRotation() != getRotation()) + _needsInitialSimulation // || + // _model->getTranslation() != getPosition() || + // _model->getRotation() != getRotation() + ) && _model->isActive() && _dimensionsInitialized) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c458c2e1f8..b6487103d1 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include @@ -30,6 +32,11 @@ static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; +// NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates +// to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving +// just under this velocity threshold would trigger an update about V/dX times per second. +const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec + #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS bool EntityMotionState::entityTreeIsLocked() const { EntityTreeElementPointer element = _entity ? _entity->getElement() : nullptr; @@ -548,6 +555,10 @@ void EntityMotionState::resetMeasuredBodyAcceleration() { _lastMeasureStep = ObjectMotionState::getWorldSimulationStep(); if (_body) { _lastVelocity = getBodyLinearVelocity(); + // if _lastVelocity is too slow, set it to zero + if (glm::length2(_lastVelocity) < MIN_LINEAR_SPEED_SQUARED) { + _lastVelocity *= 0.0f; + } } else { _lastVelocity = glm::vec3(0.0f); } @@ -567,6 +578,10 @@ void EntityMotionState::measureBodyAcceleration() { // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt glm::vec3 velocity = getBodyLinearVelocity(); + if (glm::length2(velocity) < MIN_LINEAR_SPEED_SQUARED) { + velocity *= 0.0f; + } + _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _lastVelocity = velocity; if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index d2c152d876..8bd6c3c983 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -80,16 +80,8 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { } glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { - // returns the body's velocity unless it is moving too slow in which case returns zero + // returns the body's velocity btVector3 velocity = _body->getLinearVelocity(); - - // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates - // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving - // just under this velocity threshold would trigger an update about V/dX times per second. - const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec - if (velocity.length2() < MIN_LINEAR_SPEED_SQUARED) { - velocity *= 0.0f; - } return bulletToGLM(velocity); } From fbe3cb9511c8d2e143e23e0dd7f3dad668d8f72e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 09:56:51 -0700 Subject: [PATCH 0218/1003] try another way of fixing held object snagging at slow velocity --- libraries/physics/src/EntityMotionState.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index b6487103d1..2290f5832e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -256,6 +256,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverPosition = bulletToGLM(xform.getOrigin()); _serverRotation = bulletToGLM(xform.getRotation()); _serverVelocity = getBodyLinearVelocity(); + if (glm::length2(_serverVelocity) < MIN_LINEAR_SPEED_SQUARED) { + _serverVelocity *= 0.0f; + } + _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); From 22602fb6f6506c5af6f7fa04ca51573c41d393bd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 10:00:16 -0700 Subject: [PATCH 0219/1003] 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 26dcaeb0562af7ce4c52c9abf7cd65a83f307b8c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 10:10:43 -0700 Subject: [PATCH 0220/1003] try another way of fixing held object snagging at slow velocity --- libraries/physics/src/EntityMotionState.cpp | 7 ++++++- libraries/physics/src/ObjectMotionState.cpp | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 2290f5832e..a8194f76e3 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -194,7 +194,12 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setRotation(bulletToGLM(worldTrans.getRotation())); - _entity->setVelocity(getBodyLinearVelocity()); + glm::vec3 velocity = getBodyLinearVelocity(); + if (glm::length2(velocity) < MIN_LINEAR_SPEED_SQUARED) { + velocity *= 0.0f; + } + _entity->setVelocity(velocity); + _entity->setAngularVelocity(getBodyAngularVelocity()); _entity->setLastSimulated(usecTimestampNow()); diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 8bd6c3c983..7a384e32d1 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -80,9 +80,7 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { } glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { - // returns the body's velocity - btVector3 velocity = _body->getLinearVelocity(); - return bulletToGLM(velocity); + return bulletToGLM(_body->getLinearVelocity()); } glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { From 2bdcb256d8103fa667304dce8b46cba687a4c691 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 10:26:37 -0700 Subject: [PATCH 0221/1003] try another way of fixing held object snagging at slow velocity --- libraries/physics/src/EntityMotionState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index a8194f76e3..6ddb8b6b45 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -302,7 +302,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverPosition += dt * _serverVelocity; } - if (_serverActionData != _entity->getActionData()) { + if (_serverActionData != _entity->getActionData() || _entity->getActionDataDirty()) { setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); return true; } From c5a5f26e6ac8fe93351576e5a51fa843e9f056f1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 10:27:35 -0700 Subject: [PATCH 0222/1003] try another way of fixing held object snagging at slow velocity --- libraries/entities/src/EntityItem.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index f438e3f28b..3560097083 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -407,6 +407,7 @@ public: QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); void setActionDataDirty(bool value) const { _actionDataDirty = value; } + bool getActionDataDirty(bool value) const { return _actionDataDirty; } bool shouldSuppressLocationEdits() const; protected: From 5c031a38c78748486d243dccdc73bdf92713ff8b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 10:29:30 -0700 Subject: [PATCH 0223/1003] try another way of fixing held object snagging at slow velocity --- libraries/entities/src/EntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3560097083..9ecc1d25a0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -407,7 +407,7 @@ public: QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); void setActionDataDirty(bool value) const { _actionDataDirty = value; } - bool getActionDataDirty(bool value) const { return _actionDataDirty; } + bool getActionDataDirty() const { return _actionDataDirty; } bool shouldSuppressLocationEdits() const; protected: From 46e5bf043599be5c4c5c2cf959023b0129bace62 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 11:59:03 -0700 Subject: [PATCH 0224/1003] take entity-server clock-skew into account when handling action expiration times --- interface/src/InterfaceActionFactory.cpp | 1 + interface/src/avatar/AvatarActionHold.cpp | 3 ++- libraries/entities/src/EntityItem.cpp | 6 ++++++ libraries/entities/src/EntityItem.h | 7 ++++++- libraries/physics/src/EntityMotionState.cpp | 2 +- libraries/physics/src/ObjectAction.cpp | 16 ++++++++++++++++ libraries/physics/src/ObjectAction.h | 2 ++ libraries/physics/src/ObjectActionOffset.cpp | 3 ++- libraries/physics/src/ObjectActionSpring.cpp | 3 ++- 9 files changed, 38 insertions(+), 5 deletions(-) diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp index cf5dad8fa5..f814df9a99 100644 --- a/interface/src/InterfaceActionFactory.cpp +++ b/interface/src/InterfaceActionFactory.cpp @@ -66,6 +66,7 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt if (action) { action->deserialize(data); if (action->lifetimeIsOver()) { + qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation"; return nullptr; } } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 0264777164..4d07ecb5e3 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -243,7 +243,7 @@ QByteArray AvatarActionHold::serialize() const { dataStream << _linearTimeScale; dataStream << _hand; - dataStream << _expires; + dataStream << _expires + getEntityServerClockSkew(); dataStream << _tag; }); @@ -276,6 +276,7 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) { dataStream >> _hand; dataStream >> _expires; + _expires -= getEntityServerClockSkew(); dataStream >> _tag; #if WANT_DEBUG diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5b262b273b..9b3821b56a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -342,6 +342,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef "ERROR CASE...args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU"; return 0; } + setSourceUUID(args.sourceUUID); args.entitiesPerPacket++; @@ -1534,6 +1535,8 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi if (success) { _allActionsDataCache = newDataCache; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + } else { + qDebug() << "EntityItem::addActionInternal -- serializeActions failed"; } return success; } @@ -1628,6 +1631,7 @@ void EntityItem::deserializeActionsInternal() { quint64 now = usecTimestampNow(); if (!_element) { + qDebug() << "EntityItem::deserializeActionsInternal -- no _element"; return; } @@ -1670,6 +1674,8 @@ void EntityItem::deserializeActionsInternal() { if (action) { entity->addActionInternal(simulation, action); action->locallyAddedButNotYetReceived = false; + } else { + qDebug() << "EntityItem::deserializeActionsInternal -- action creation failed"; } } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9ecc1d25a0..858dc7e326 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -407,9 +407,12 @@ public: QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); void setActionDataDirty(bool value) const { _actionDataDirty = value; } - bool getActionDataDirty() const { return _actionDataDirty; } bool shouldSuppressLocationEdits() const; + void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; } + const QUuid& getSourceUUID() const { return _sourceUUID; } + bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } + protected: const QByteArray getActionDataInternal() const; @@ -510,6 +513,8 @@ protected: // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; + + QUuid _sourceUUID; /// the server node UUID we came from }; #endif // hifi_EntityItem_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6ddb8b6b45..a8194f76e3 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -302,7 +302,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverPosition += dt * _serverVelocity; } - if (_serverActionData != _entity->getActionData() || _entity->getActionDataDirty()) { + if (_serverActionData != _entity->getActionData()) { setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); return true; } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 2f0de6d0ab..10a3d8d110 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -62,6 +62,22 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta updateActionWorker(deltaTimeStep); } +int ObjectAction::getEntityServerClockSkew() { + auto nodeList = DependencyManager::get(); + + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return 0; + } + + const QUuid& entityServerNodeID = ownerEntity->getSourceUUID(); + auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID); + if (entityServerNode) { + return entityServerNode->getClockSkewUsec(); + } + return 0; +} + bool ObjectAction::updateArguments(QVariantMap arguments) { bool somethingChanged = false; diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 5c29ac9892..4a531ea70d 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -50,6 +50,8 @@ public: protected: + int getEntityServerClockSkew(); + virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); virtual void setPosition(glm::vec3 position); diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index d01178dcc3..b6edf22ffc 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -160,7 +160,7 @@ QByteArray ObjectActionOffset::serialize() const { dataStream << _linearDistance; dataStream << _linearTimeScale; dataStream << _positionalTargetSet; - dataStream << _expires; + dataStream << _expires + getEntityServerClockSkew(); dataStream << _tag; }); @@ -190,6 +190,7 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) { dataStream >> _linearTimeScale; dataStream >> _positionalTargetSet; dataStream >> _expires; + _expires -= getEntityServerClockSkew(); dataStream >> _tag; _active = true; }); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index a99cfdd747..a74756eb53 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -198,7 +198,7 @@ QByteArray ObjectActionSpring::serialize() const { dataStream << _rotationalTarget; dataStream << _angularTimeScale; dataStream << _rotationalTargetSet; - dataStream << _expires; + dataStream << _expires + getEntityServerClockSkew(); dataStream << _tag; }); @@ -233,6 +233,7 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { dataStream >> _rotationalTargetSet; dataStream >> _expires; + _expires -= getEntityServerClockSkew(); dataStream >> _tag; _active = true; From 8c229d88a8e30f5a9d0815d7aa3811b4ec5463a7 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 19 Oct 2015 11:59:44 -0700 Subject: [PATCH 0225/1003] moving whitelist code out of octree --- libraries/entities/src/EntityTree.cpp | 46 +++++++++++++++++++++++++++ libraries/entities/src/EntityTree.h | 8 +++++ libraries/octree/src/Octree.cpp | 24 +------------- libraries/octree/src/Octree.h | 2 +- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6e487cbfe3..d40e4e6e96 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -448,6 +448,52 @@ bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extr // if this element doesn't contain the point, then none of its children can contain the point, so stop searching return false; } +// combines the ray cast arguments into a single object +class RayArgs { +public: + glm::vec3 origin; + glm::vec3 direction; + OctreeElementPointer& element; + float& distance; + BoxFace& face; + glm::vec3& surfaceNormal; + const QVector& entityIdsToInclude; + void** intersectedObject; + bool found; + bool precisionPicking; +}; + + +bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { + RayArgs* args = static_cast(extraData); + bool keepSearching = true; + if (element->findRayIntersection(args->origin, args->direction, keepSearching, + args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, + args->intersectedObject, args->precisionPicking)) { + args->found = true; + } + return keepSearching; +} + +bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, + Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking }; + distance = FLT_MAX; + + bool requireLock = lockType == Octree::Lock; + bool lockResult = withReadLock([&]{ + recurseTreeWithOperation(findRayIntersectionOp, &args); + }, requireLock); + + if (accurateResult) { + *accurateResult = lockResult; // if user asked to accuracy or result, let them know this is accurate + } + + return args.found; +} + EntityItemPointer EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) { FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX }; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index ff84a79088..17c7347b2e 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -80,6 +80,14 @@ public: virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude = QVector(), + void** intersectedObject = NULL, + Octree::lockType lockType = Octree::TryLock, + bool* accurateResult = NULL, + bool precisionPicking = false); + virtual bool rootElementHasData() const { return true; } // the root at least needs to store the number of entities in the packet/buffer diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3b6467401c..f573161255 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -709,34 +709,12 @@ public: bool precisionPicking; }; -bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { - RayArgs* args = static_cast(extraData); - bool keepSearching = true; - if (element->findRayIntersection(args->origin, args->direction, keepSearching, - args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->intersectedObject, args->precisionPicking)) { - args->found = true; - } - return keepSearching; -} bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking}; - distance = FLT_MAX; - - bool requireLock = lockType == Octree::Lock; - bool lockResult = withReadLock([&]{ - recurseTreeWithOperation(findRayIntersectionOp, &args); - }, requireLock); - - if (accurateResult) { - *accurateResult = lockResult; // if user asked to accuracy or result, let them know this is accurate - } - - return args.found; + return false; } class SphereArgs { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index f0a4d2c9c0..c785bf47fb 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -298,7 +298,7 @@ public: TryLock } lockType; - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude = QVector(), void** intersectedObject = NULL, From f96e9eb1e80d1b47a6ddf493d27e3100e8d8a67d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 12:04:55 -0700 Subject: [PATCH 0226/1003] fuck you, const! --- libraries/physics/src/ObjectAction.cpp | 2 +- libraries/physics/src/ObjectAction.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 10a3d8d110..ce4ebfd22f 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -62,7 +62,7 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta updateActionWorker(deltaTimeStep); } -int ObjectAction::getEntityServerClockSkew() { +int ObjectAction::getEntityServerClockSkew() const { auto nodeList = DependencyManager::get(); auto ownerEntity = _ownerEntity.lock(); diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 4a531ea70d..98e58475c6 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -50,7 +50,7 @@ public: protected: - int getEntityServerClockSkew(); + int getEntityServerClockSkew() const; virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); From ac990056021a8733a6422d5d921d9e8fac8fbf0e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 12:13:08 -0700 Subject: [PATCH 0227/1003] reverting a change to try to see sticking problem again --- libraries/physics/src/ObjectMotionState.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 7a384e32d1..144ba1e100 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -80,7 +80,19 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { } glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { - return bulletToGLM(_body->getLinearVelocity()); + + btVector3 velocity = _body->getLinearVelocity(); + + // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates + // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving + // just under this velocity threshold would trigger an update about V/dX times per second. + const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec + if (velocity.length2() < MIN_LINEAR_SPEED_SQUARED) { + velocity *= 0.0f; + } + return bulletToGLM(velocity); + + // return bulletToGLM(_body->getLinearVelocity()); } glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { From 113321184d58a4ce081773017adfa5ed9a659dda Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 12:21:26 -0700 Subject: [PATCH 0228/1003] if model RegistrationPoint doesn't match entityItem's, update it --- .../src/RenderableModelEntityItem.cpp | 12 ++++++------ libraries/physics/src/EntityMotionState.cpp | 9 +-------- libraries/physics/src/ObjectMotionState.cpp | 14 +------------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e028ee89ea..e604bdb925 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -299,22 +299,22 @@ void RenderableModelEntityItem::render(RenderArgs* args) { bool movingOrAnimating = isMoving() || isAnimatingSomething(); if ((movingOrAnimating || - _needsInitialSimulation // || - // _model->getTranslation() != getPosition() || - // _model->getRotation() != getRotation() - ) + _needsInitialSimulation || + _model->getTranslation() != getPosition() || + _model->getRotation() != getRotation() || + _model->getRegistrationPoint() != getRegistrationPoint()) && _model->isActive() && _dimensionsInitialized) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); - + // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } - + _needsInitialSimulation = false; } } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index a8194f76e3..1745984add 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -193,15 +193,8 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { measureBodyAcceleration(); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setRotation(bulletToGLM(worldTrans.getRotation())); - - glm::vec3 velocity = getBodyLinearVelocity(); - if (glm::length2(velocity) < MIN_LINEAR_SPEED_SQUARED) { - velocity *= 0.0f; - } - _entity->setVelocity(velocity); - + _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); - _entity->setLastSimulated(usecTimestampNow()); if (_entity->getSimulatorID().isNull()) { diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 144ba1e100..7a384e32d1 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -80,19 +80,7 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { } glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { - - btVector3 velocity = _body->getLinearVelocity(); - - // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates - // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving - // just under this velocity threshold would trigger an update about V/dX times per second. - const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec - if (velocity.length2() < MIN_LINEAR_SPEED_SQUARED) { - velocity *= 0.0f; - } - return bulletToGLM(velocity); - - // return bulletToGLM(_body->getLinearVelocity()); + return bulletToGLM(_body->getLinearVelocity()); } glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { From 3d8b7f9d108721605821fbc853108261f88ecb70 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 19 Oct 2015 12:56:06 -0700 Subject: [PATCH 0229/1003] 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 dcb37ccd73c41aa85164500ee5dd94dd3e9d64a1 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 19 Oct 2015 13:14:19 -0700 Subject: [PATCH 0230/1003] Removed whitelist and raypicking code from octree, fixed a small bug with polylines --- libraries/entities/src/EntityTree.cpp | 3 +- libraries/entities/src/EntityTreeElement.cpp | 41 +++++++++++++ libraries/entities/src/EntityTreeElement.h | 5 +- libraries/entities/src/PolyLineEntityItem.cpp | 7 ++- libraries/octree/src/Octree.cpp | 21 ------- libraries/octree/src/Octree.h | 7 --- libraries/octree/src/OctreeElement.cpp | 58 ------------------- libraries/octree/src/OctreeElement.h | 10 ---- 8 files changed, 51 insertions(+), 101 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d40e4e6e96..e45e3e8a1e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -467,7 +467,8 @@ public: bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; - if (element->findRayIntersection(args->origin, args->direction, keepSearching, + EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); + if (entityTreeElementPointer ->findRayIntersection(args->origin, args->direction, keepSearching, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->intersectedObject, args->precisionPicking)) { args->found = true; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 56ab27836c..b3571097b3 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -493,6 +493,47 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 return false; } +bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + void** intersectedObject, bool precisionPicking) { + + keepSearching = true; // assume that we will continue searching after this. + + float distanceToElementCube = std::numeric_limits::max(); + float distanceToElementDetails = distance; + BoxFace localFace; + glm::vec3 localSurfaceNormal; + + // if the ray doesn't intersect with our cube, we can stop searching! + if (!_cube.findRayIntersection(origin, direction, distanceToElementCube, localFace, localSurfaceNormal)) { + keepSearching = false; // no point in continuing to search + return false; // we did not intersect + } + + // by default, we only allow intersections with leaves with content + if (!canRayIntersect()) { + return false; // we don't intersect with non-leaves, and we keep searching + } + + // if the distance to the element cube is not less than the current best distance, then it's not possible + // for any details inside the cube to be closer so we don't need to consider them. + if (_cube.contains(origin) || distanceToElementCube < distance) { + + if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails, + face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) { + + if (distanceToElementDetails < distance) { + distance = distanceToElementDetails; + face = localFace; + surfaceNormal = localSurfaceNormal; + return true; + } + } + } + return false; +} + bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 0a47542e65..78537dca3f 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -142,11 +142,14 @@ public: virtual bool deleteApproved() const { return !hasEntities(); } virtual bool canRayIntersect() const { return hasEntities(); } + virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElementPointer& node, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube); - virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 352d0425bf..f1be431ce8 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; } @@ -123,7 +123,8 @@ bool PolyLineEntityItem::setNormals(const QVector& normals) { _vertices.clear(); glm::vec3 v1, v2, tangent, binormal, point; - for (int i = 0; i < minVectorSize - 1; i++) { + int finalIndex = minVectorSize -1; + for (int i = 0; i < finalIndex; i++) { float width = _strokeWidths.at(i); point = _points.at(i); @@ -138,7 +139,7 @@ bool PolyLineEntityItem::setNormals(const QVector& normals) { _vertices << v1 << v2; } //for last point we can just assume binormals are same since it represents last two vertices of quad - point = _points.at(minVectorSize - 1); + point = _points.at(finalIndex); v1 = point + binormal; v2 = point - binormal; _vertices << v1 << v2; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index f573161255..cceb3ba706 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -694,29 +694,8 @@ OctreeElementPointer Octree::getOrCreateChildElementContaining(const AACube& box return getRoot()->getOrCreateChildElementContaining(box); } -// combines the ray cast arguments into a single object -class RayArgs { -public: - glm::vec3 origin; - glm::vec3 direction; - OctreeElementPointer& element; - float& distance; - BoxFace& face; - glm::vec3& surfaceNormal; - const QVector& entityIdsToInclude; - void** intersectedObject; - bool found; - bool precisionPicking; -}; -bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, - Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - return false; -} - class SphereArgs { public: glm::vec3 center; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index c785bf47fb..5ebb991d49 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -298,13 +298,6 @@ public: TryLock } lockType; - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude = QVector(), - void** intersectedObject = NULL, - Octree::lockType lockType = Octree::TryLock, - bool* accurateResult = NULL, - bool precisionPicking = false); bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 9a48079338..17ebe492ce 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -573,64 +573,6 @@ void OctreeElement::notifyUpdateHooks() { } } -bool OctreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject, bool precisionPicking) { - - keepSearching = true; // assume that we will continue searching after this. - - float distanceToElementCube = std::numeric_limits::max(); - float distanceToElementDetails = distance; - BoxFace localFace; - glm::vec3 localSurfaceNormal; - - // if the ray doesn't intersect with our cube, we can stop searching! - if (!_cube.findRayIntersection(origin, direction, distanceToElementCube, localFace, localSurfaceNormal)) { - keepSearching = false; // no point in continuing to search - return false; // we did not intersect - } - - // by default, we only allow intersections with leaves with content - if (!canRayIntersect()) { - return false; // we don't intersect with non-leaves, and we keep searching - } - - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. - if (_cube.contains(origin) || distanceToElementCube < distance) { - - if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails, - face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) { - - if (distanceToElementDetails < distance) { - distance = distanceToElementDetails; - face = localFace; - surfaceNormal = localSurfaceNormal; - return true; - } - } - } - return false; -} - -bool OctreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject, bool precisionPicking, float distanceToElementCube) { - - // we did hit this element, so calculate appropriate distances - if (hasContent()) { - element = shared_from_this(); - distance = distanceToElementCube; - if (intersectedObject) { - *intersectedObject = this; - } - keepSearching = false; - return true; // we did intersect - } - return false; // we did not intersect -} bool OctreeElement::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const { diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index c132d437cd..6ccef31d4f 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -118,16 +118,6 @@ public: virtual bool deleteApproved() const { return true; } virtual bool canRayIntersect() const { return isLeaf(); } - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject = NULL, bool precisionPicking = false); - - virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject, bool precisionPicking, float distanceToElementCube); - /// \param center center of sphere in meters /// \param radius radius of sphere in meters /// \param[out] penetration pointing into cube from sphere From 62c89eaf7d60df820bc1e799150366ed21dbd17c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 19 Oct 2015 10:53:00 -0700 Subject: [PATCH 0231/1003] Joint rotation fix for FBX files exported from Blender. This isn't a great fix. But it will at least allow people to export avatars from Blender. --- libraries/fbx/src/FBXReader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index ca9f126cba..1eda5304e4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -745,10 +745,18 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html model.translation = translation; + model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); model.preRotation = glm::quat(glm::radians(preRotation)); model.rotation = glm::quat(glm::radians(rotation)); model.postRotation = glm::quat(glm::radians(postRotation)); + + if (geometry.applicationName.startsWith("Blender")) { + // blender puts the jointOffset in the wrong place. + model.preRotation = model.rotation; + model.rotation = glm::quat(); + } + model.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); // NOTE: angles from the FBX file are in degrees From 6f30a3c17856c2120a440e5a3cb1fa45319035c1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 13:56:30 -0700 Subject: [PATCH 0232/1003] new method -- ObjectMotionState::getBodyLinearVelocityGTSigma --- libraries/physics/src/EntityMotionState.cpp | 22 +++------------------ libraries/physics/src/ObjectMotionState.cpp | 13 ++++++++++++ libraries/physics/src/ObjectMotionState.h | 1 + 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 1745984add..6832b8daff 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -32,11 +32,6 @@ static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4; const uint32_t LOOPS_FOR_SIMULATION_ORPHAN = 50; const quint64 USECS_BETWEEN_OWNERSHIP_BIDS = USECS_PER_SECOND / 5; -// NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates -// to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving -// just under this velocity threshold would trigger an update about V/dX times per second. -const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec - #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS bool EntityMotionState::entityTreeIsLocked() const { EntityTreeElementPointer element = _entity ? _entity->getElement() : nullptr; @@ -253,11 +248,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { btTransform xform = _body->getWorldTransform(); _serverPosition = bulletToGLM(xform.getOrigin()); _serverRotation = bulletToGLM(xform.getRotation()); - _serverVelocity = getBodyLinearVelocity(); - if (glm::length2(_serverVelocity) < MIN_LINEAR_SPEED_SQUARED) { - _serverVelocity *= 0.0f; - } - + _serverVelocity = getBodyLinearVelocityGTSigma(); _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); @@ -556,11 +547,7 @@ void EntityMotionState::bump(quint8 priority) { void EntityMotionState::resetMeasuredBodyAcceleration() { _lastMeasureStep = ObjectMotionState::getWorldSimulationStep(); if (_body) { - _lastVelocity = getBodyLinearVelocity(); - // if _lastVelocity is too slow, set it to zero - if (glm::length2(_lastVelocity) < MIN_LINEAR_SPEED_SQUARED) { - _lastVelocity *= 0.0f; - } + _lastVelocity = getBodyLinearVelocityGTSigma(); } else { _lastVelocity = glm::vec3(0.0f); } @@ -579,10 +566,7 @@ void EntityMotionState::measureBodyAcceleration() { // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt - glm::vec3 velocity = getBodyLinearVelocity(); - if (glm::length2(velocity) < MIN_LINEAR_SPEED_SQUARED) { - velocity *= 0.0f; - } + glm::vec3 velocity = getBodyLinearVelocityGTSigma(); _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _lastVelocity = velocity; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 7a384e32d1..4f3d0396c6 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -83,6 +83,19 @@ glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { return bulletToGLM(_body->getLinearVelocity()); } +glm::vec3 ObjectMotionState::getBodyLinearVelocityGTSigma() const { + // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates + // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving + // just under this velocity threshold would trigger an update about V/dX times per second. + const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec + + glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); + if (glm::length2(velocity) < MIN_LINEAR_SPEED_SQUARED) { + velocity *= 0.0f; + } + return velocity; +} + glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { return glm::vec3(0.0f); // Subclasses override where meaningful. } diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ec98f8858f..450ac34a90 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -89,6 +89,7 @@ public: void setBodyGravity(const glm::vec3& gravity) const; glm::vec3 getBodyLinearVelocity() const; + glm::vec3 getBodyLinearVelocityGTSigma() const; glm::vec3 getBodyAngularVelocity() const; virtual glm::vec3 getObjectLinearVelocityChange() const; From 8fbf081223396acd9bf1a46a04a04b54065d6a5c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 14:51:18 -0700 Subject: [PATCH 0233/1003] git rid of sphere test --- examples/controllers/handControllerGrab.js | 113 ++++++++++----------- 1 file changed, 53 insertions(+), 60 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index faa90efdff..e6fedd8f25 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -34,18 +34,18 @@ var NO_INTERSECT_COLOR = { red: 10, green: 10, blue: 255}; // line color when pi var INTERSECT_COLOR = { red: 250, green: 10, blue: 10}; // line color when pick hits var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000,z: 1000}; var LINE_LENGTH = 500; - +var PICK_MAX_DISTANCE = 500; // max length of pick-ray ///////////////////////////////////////////////////////////////// // // near grabbing // -var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.6; // max length of pick-ray for close grabbing to be selected +var NEAR_PICK_MAX_DISTANCE = 0.1; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things +var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object ///////////////////////////////////////////////////////////////// // @@ -256,12 +256,18 @@ function MyController(hand, triggerAction) { // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); - var pickRay = { + var distantPickRay = { origin: handPosition, - direction: Quat.getUp(this.getHandRotation()) + direction: Quat.getUp(this.getHandRotation()), + length: PICK_MAX_DISTANCE + }; + var palmPickRay = { + origin: handPosition, + direction: Quat.getFront(this.getHandRotation()), + length: NEAR_PICK_MAX_DISTANCE }; - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); // don't pick 60x per second. do this check after updating the line so it's not jumpy. var now = Date.now(); @@ -270,67 +276,54 @@ function MyController(hand, triggerAction) { } this.lastPickTime = now; - var intersection = Entities.findRayIntersection(pickRay, true); - if (intersection.intersects && intersection.properties.locked === 0) { - // the ray is intersecting something we can move. - var handControllerPosition = Controller.getSpatialControlPosition(this.palm); - var intersectionDistance = Vec3.distance(handControllerPosition, intersection.intersection); - this.grabbedEntity = intersection.entityID; + var pickRays = [distantPickRay, palmPickRay]; + for (var index=0; index < pickRays.length; ++index) { + var pickRay = pickRays[index]; + var directionNormalized = Vec3.normalize(pickRay.direction); + var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); + var pickRayBacked = { + origin: Vec3.subtract(pickRay.origin, directionBacked), + direction: pickRay.direction + }; - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { - this.grabbedEntity = null; - return; - } - if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) { - // the hand is very close to the intersected object. go into close-grabbing mode. - if (intersection.properties.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else { - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } - } else { - // don't allow two people to distance grab the same object - if (entityIsGrabbedByOther(intersection.entityID)) { + var intersection = Entities.findRayIntersection(pickRayBacked, true); + if (intersection.intersects && intersection.properties.locked === 0) { + // the ray is intersecting something we can move. + var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + this.grabbedEntity = intersection.entityID; + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, + intersection.entityID, + DEFAULT_GRABBABLE_DATA); + if (grabbableData.grabbable === false) { this.grabbedEntity = null; - } else { - // the hand is far from the intersected object. go into distance-holding mode + continue; + } + if (intersectionDistance > pickRay.length) { + // too far away for this ray. + continue; + } + if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + // the hand is very close to the intersected object. go into close-grabbing mode. if (intersection.properties.collisionsWillMove === 1) { - this.setState(STATE_DISTANCE_HOLDING); + this.setState(STATE_NEAR_GRABBING); } else { - this.setState(STATE_FAR_GRABBING_NON_COLLIDING); + this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } + } else { + // don't allow two people to distance grab the same object + if (entityIsGrabbedByOther(intersection.entityID)) { + this.grabbedEntity = null; + } else { + // the hand is far from the intersected object. go into distance-holding mode + if (intersection.properties.collisionsWillMove === 1) { + this.setState(STATE_DISTANCE_HOLDING); + } else { + this.setState(STATE_FAR_GRABBING_NON_COLLIDING); + } } } } - } else { - // forward ray test failed, try sphere test. - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = GRAB_RADIUS; - var i, props, distance; - - for (i = 0; i < nearbyEntities.length; i++) { - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { - return; - } - - props = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]); - - distance = Vec3.distance(props.position, handPosition); - if (distance < minDistance && props.name !== "pointer") { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - } - } - if (this.grabbedEntity === null) { - // this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } else if (props.locked === 0 && props.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else if (props.collisionsWillMove === 0) { - // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } } }; From 917bfbf64e4a1dc81f0693ab597ada3f7e76f328 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 19 Oct 2015 15:19:32 -0700 Subject: [PATCH 0234/1003] hack to reduce hand influence of hips in HMD mode --- libraries/animation/src/AnimInverseKinematics.cpp | 2 +- libraries/animation/src/IKTarget.h | 9 +++++++-- libraries/animation/src/RotationAccumulator.cpp | 4 ++-- libraries/animation/src/RotationAccumulator.h | 4 +++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 17a1af1969..516d4116d8 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -255,7 +255,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector -void RotationAccumulator::add(const glm::quat& rotation) { +void RotationAccumulator::add(const glm::quat& rotation, float weight) { // make sure both quaternions are on the same hyper-hemisphere before we add them linearly (lerp) - _rotationSum += copysignf(1.0f, glm::dot(_rotationSum, rotation)) * rotation; + _rotationSum += copysignf(weight, glm::dot(_rotationSum, rotation)) * rotation; ++_numRotations; _isDirty = true; } diff --git a/libraries/animation/src/RotationAccumulator.h b/libraries/animation/src/RotationAccumulator.h index 87b1a753c9..a113b086f9 100644 --- a/libraries/animation/src/RotationAccumulator.h +++ b/libraries/animation/src/RotationAccumulator.h @@ -18,7 +18,9 @@ public: int size() const { return _numRotations; } - void add(const glm::quat& rotation); + /// \param rotation rotation to add + /// \param weight contribution factor of this rotation to total accumulation + void add(const glm::quat& rotation, float weight = 1.0f); glm::quat getAverage(); From 5a968e3c96ff4db13414d50040b147053c385f3f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 15:20:04 -0700 Subject: [PATCH 0235/1003] tweak --- 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 e6fedd8f25..01c02840cb 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -43,9 +43,9 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.1; // max length of pick-ray for close grabbing to be selected +var NEAR_PICK_MAX_DISTANCE = 0.2; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things -var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var PICK_BACKOFF_DISTANCE = 0.1; // helps when hand is intersecting the grabble object ///////////////////////////////////////////////////////////////// // From 110adc5c227e983e880ad69882895e6e802d4054 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 15:21:25 -0700 Subject: [PATCH 0236/1003] fix model LOD behavior --- interface/resources/qml/Stats.qml | 24 ++++++++++++++++--- interface/src/LODManager.cpp | 14 ++++++++--- interface/src/ui/Stats.cpp | 8 +++++-- interface/src/ui/Stats.h | 16 +++++++++---- .../render-utils/src/MeshPartPayload.cpp | 8 +++---- libraries/render-utils/src/MeshPartPayload.h | 3 --- .../render-utils/src/RenderDeferredTask.cpp | 6 +++-- libraries/render/src/render/DrawTask.cpp | 23 +++++++++++++++++- libraries/render/src/render/DrawTask.h | 22 ++++++++++++++++- 9 files changed, 100 insertions(+), 24 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 780f9cb89f..c98d4741b0 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -181,15 +181,33 @@ Item { color: root.fontColor; font.pixelSize: root.fontSize visible: root.expanded; - text: "\tMesh Parts Rendered Opaque: " + root.meshOpaque + - " / Translucent: " + root.meshTranslucent; + text: "\tItems Rendered Opaque: " + root.opaqueRendered + + " / Translucent: " + root.translucentRendered + + " / Other: " + root.otherRendered; } Text { color: root.fontColor; font.pixelSize: root.fontSize visible: root.expanded; text: "\tOpaque considered: " + root.opaqueConsidered + - " / Out of view: " + root.opaqueOutOfView + " / Too small: " + root.opaqueTooSmall; + " / Out of view: " + root.opaqueOutOfView + + " / Too small: " + root.opaqueTooSmall; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "\tTranslucent considered: " + root.translucentConsidered + + " / Out of view: " + root.translucentOutOfView + + " / Too small: " + root.translucentTooSmall; + } + Text { + color: root.fontColor; + font.pixelSize: root.fontSize + visible: root.expanded; + text: "\tOther considered: " + root.otherConsidered + + " / Out of view: " + root.otherOutOfView + + " / Too small: " + root.otherTooSmall; } Text { color: root.fontColor; diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 2a2edef831..0d2681ac72 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -229,7 +229,8 @@ bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { static bool shouldRenderTableNeedsBuilding = true; static QMap shouldRenderTable; if (shouldRenderTableNeedsBuilding) { - + qDebug() << "LODManager::shouldRender() rebuilding table!"; + float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small float scale = maxScale; float factor = 1.0f; @@ -254,8 +255,15 @@ bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { if (closestScale < largestDimension) { visibleDistanceAtClosestScale *= 2.0f; } - - return distanceToCamera <= visibleDistanceAtClosestScale; + + bool result = distanceToCamera <= visibleDistanceAtClosestScale; + + /* + qDebug() << "LODManager::shouldRender() bounds:" << bounds << "result:" << result + << "distanceToCamera:" << distanceToCamera << "visibleDistanceAtClosestScale:" << visibleDistanceAtClosestScale; + */ + + return result; }; // TODO: This is essentially the same logic used to render octree cells, but since models are more detailed then octree cells diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 7e2143c454..1c0c03e16c 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -342,14 +342,18 @@ void Stats::setRenderDetails(const RenderDetails& details) { STAT_UPDATE(triangles, details._trianglesRendered); STAT_UPDATE(materialSwitches, details._materialSwitches); if (_expanded) { - STAT_UPDATE(meshOpaque, details._opaque._rendered); - STAT_UPDATE(meshTranslucent, details._opaque._rendered); STAT_UPDATE(opaqueConsidered, details._opaque._considered); STAT_UPDATE(opaqueOutOfView, details._opaque._outOfView); STAT_UPDATE(opaqueTooSmall, details._opaque._tooSmall); + STAT_UPDATE(opaqueRendered, details._opaque._rendered); STAT_UPDATE(translucentConsidered, details._translucent._considered); STAT_UPDATE(translucentOutOfView, details._translucent._outOfView); STAT_UPDATE(translucentTooSmall, details._translucent._tooSmall); + STAT_UPDATE(translucentRendered, details._translucent._rendered); + STAT_UPDATE(otherConsidered, details._other._considered); + STAT_UPDATE(otherOutOfView, details._other._outOfView); + STAT_UPDATE(otherTooSmall, details._other._tooSmall); + STAT_UPDATE(otherRendered, details._other._rendered); } } diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f1426a9e37..39e3c6b24a 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -58,14 +58,18 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, triangles, 0) STATS_PROPERTY(int, quads, 0) STATS_PROPERTY(int, materialSwitches, 0) - STATS_PROPERTY(int, meshOpaque, 0) - STATS_PROPERTY(int, meshTranslucent, 0) STATS_PROPERTY(int, opaqueConsidered, 0) STATS_PROPERTY(int, opaqueOutOfView, 0) STATS_PROPERTY(int, opaqueTooSmall, 0) + STATS_PROPERTY(int, opaqueRendered, 0) STATS_PROPERTY(int, translucentConsidered, 0) STATS_PROPERTY(int, translucentOutOfView, 0) STATS_PROPERTY(int, translucentTooSmall, 0) + STATS_PROPERTY(int, translucentRendered, 0) + STATS_PROPERTY(int, otherConsidered, 0) + STATS_PROPERTY(int, otherOutOfView, 0) + STATS_PROPERTY(int, otherTooSmall, 0) + STATS_PROPERTY(int, otherRendered, 0) STATS_PROPERTY(QString, sendingMode, QString()) STATS_PROPERTY(QString, packetStats, QString()) STATS_PROPERTY(QString, lodStatus, QString()) @@ -135,14 +139,18 @@ signals: void trianglesChanged(); void quadsChanged(); void materialSwitchesChanged(); - void meshOpaqueChanged(); - void meshTranslucentChanged(); void opaqueConsideredChanged(); void opaqueOutOfViewChanged(); void opaqueTooSmallChanged(); + void opaqueRenderedChanged(); void translucentConsideredChanged(); void translucentOutOfViewChanged(); void translucentTooSmallChanged(); + void translucentRenderedChanged(); + void otherConsideredChanged(); + void otherOutOfViewChanged(); + void otherTooSmallChanged(); + void otherRenderedChanged(); void sendingModeChanged(); void packetStatsChanged(); void lodStatusChanged(); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 54b0bacf6e..80327b7e54 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -89,11 +89,9 @@ render::ItemKey MeshPartPayload::getKey() const { } render::Item::Bound MeshPartPayload::getBound() const { - if (_isBoundInvalid) { - model->getPartBounds(meshIndex, partIndex); - _isBoundInvalid = false; - } - return _bound; + // NOTE: we can't cache this bounds because we need to handle the case of a moving + // entity or mesh part. + return model->getPartBounds(meshIndex, partIndex); } void MeshPartPayload::drawCall(gpu::Batch& batch) const { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 7e476445e6..51e577e7c7 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -55,9 +55,6 @@ public: bool _hasColorAttrib = false; bool _isSkinned = false; bool _isBlendShaped = false; - - mutable render::Item::Bound _bound; - mutable bool _isBoundInvalid = true; }; namespace render { diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index df062e3fa9..845c96372c 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -76,7 +76,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { } ) ))); - _jobs.push_back(Job(new CullItems::JobModel("CullOpaque", _jobs.back().getOutput()))); + _jobs.push_back(Job(new CullItemsOpaque::JobModel("CullOpaque", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput()))); auto& renderedOpaques = _jobs.back().getOutput(); _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput()))); @@ -105,7 +105,9 @@ RenderDeferredTask::RenderDeferredTask() : Task() { } ) ))); - _jobs.push_back(Job(new CullItems::JobModel("CullTransparent", _jobs.back().getOutput()))); + _jobs.push_back(Job(new CullItemsTransparent::JobModel("CullTransparent", _jobs.back().getOutput()))); + + _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index e2c4c5f747..877567aecd 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -115,6 +115,26 @@ void CullItems::run(const SceneContextPointer& sceneContext, const RenderContext outItems.clear(); outItems.reserve(inItems.size()); + RenderArgs* args = renderContext->args; + args->_details.pointTo(RenderDetails::OTHER_ITEM); + cullItems(sceneContext, renderContext, inItems, outItems); +} + +void CullItemsOpaque::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + + outItems.clear(); + outItems.reserve(inItems.size()); + RenderArgs* args = renderContext->args; + args->_details.pointTo(RenderDetails::OPAQUE_ITEM); + cullItems(sceneContext, renderContext, inItems, outItems); +} + +void CullItemsTransparent::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + + outItems.clear(); + outItems.reserve(inItems.size()); + RenderArgs* args = renderContext->args; + args->_details.pointTo(RenderDetails::TRANSLUCENT_ITEM); cullItems(sceneContext, renderContext, inItems, outItems); } @@ -233,9 +253,10 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext ItemIDsBounds culledItems; culledItems.reserve(inItems.size()); + RenderArgs* args = renderContext->args; + args->_details.pointTo(RenderDetails::OTHER_ITEM); cullItems(sceneContext, renderContext, inItems, culledItems); - RenderArgs* args = renderContext->args; gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; renderItems(sceneContext, renderContext, culledItems); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index b7a03b81e2..1db666a512 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -230,11 +230,31 @@ public: class CullItems { public: + CullItems() { + qDebug() << "CullItems() created... "; + } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - typedef Job::ModelIO JobModel; }; +class CullItemsOpaque { +public: + CullItemsOpaque() { + qDebug() << "CullItemsOpaque() created... "; + } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); + typedef Job::ModelIO JobModel; +}; + +class CullItemsTransparent { +public: + CullItemsTransparent() { + qDebug() << "CullItemsTransparent() created... "; + } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); + typedef Job::ModelIO JobModel; +}; + class DepthSortItems { public: bool _frontToBack = true; From 47d8afa6a8e92ad6f28ebc3f271c558fb2018b96 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 19 Oct 2015 15:22:10 -0700 Subject: [PATCH 0237/1003] Fix vhacd for models with quads --- tools/vhacd-util/src/VHACDUtil.cpp | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/tools/vhacd-util/src/VHACDUtil.cpp b/tools/vhacd-util/src/VHACDUtil.cpp index f39bea9cf9..75a1a9dc3a 100644 --- a/tools/vhacd-util/src/VHACDUtil.cpp +++ b/tools/vhacd-util/src/VHACDUtil.cpp @@ -156,29 +156,18 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) { AABox aaBox; unsigned int triangleCount = meshPart.triangleIndices.size() / 3; - for (unsigned int i = 0; i < triangleCount; i++) { - glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[i * 3]]; - glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[i * 3 + 1]]; - glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[i * 3 + 2]]; - aaBox += p0; - aaBox += p1; - aaBox += p2; + for (unsigned int i = 0; i < triangleCount; ++i) { + aaBox += mesh.vertices[meshPart.triangleIndices[i * 3]]; + aaBox += mesh.vertices[meshPart.triangleIndices[i * 3 + 1]]; + aaBox += mesh.vertices[meshPart.triangleIndices[i * 3 + 2]]; } unsigned int quadCount = meshPart.quadIndices.size() / 4; - for (unsigned int i = 0; i < quadCount; i++) { - unsigned int p0Index = meshPart.quadIndices[i * 4]; - unsigned int p1Index = meshPart.quadIndices[i * 4 + 1]; - unsigned int p2Index = meshPart.quadIndices[i * 4 + 2]; - unsigned int p3Index = meshPart.quadIndices[i * 4 + 3]; - glm::vec3 p0 = mesh.vertices[p0Index]; - glm::vec3 p1 = mesh.vertices[p1Index + 1]; - glm::vec3 p2 = mesh.vertices[p2Index + 2]; - glm::vec3 p3 = mesh.vertices[p3Index + 3]; - aaBox += p0; - aaBox += p1; - aaBox += p2; - aaBox += p3; + for (unsigned int i = 0; i < quadCount; ++i) { + aaBox += mesh.vertices[meshPart.quadIndices[i * 4]]; + aaBox += mesh.vertices[meshPart.quadIndices[i * 4 + 1]]; + aaBox += mesh.vertices[meshPart.quadIndices[i * 4 + 2]]; + aaBox += mesh.vertices[meshPart.quadIndices[i * 4 + 3]]; } return aaBox; From 0b2bf8bda8725f445b8fc59e90862d542c424e19 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 15:23:45 -0700 Subject: [PATCH 0238/1003] remove some dead code --- interface/src/LODManager.cpp | 11 +---------- libraries/render/src/render/DrawTask.h | 9 --------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 0d2681ac72..368143e36e 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -229,8 +229,6 @@ bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { static bool shouldRenderTableNeedsBuilding = true; static QMap shouldRenderTable; if (shouldRenderTableNeedsBuilding) { - qDebug() << "LODManager::shouldRender() rebuilding table!"; - float SMALLEST_SCALE_IN_TABLE = 0.001f; // 1mm is plenty small float scale = maxScale; float factor = 1.0f; @@ -256,14 +254,7 @@ bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) { visibleDistanceAtClosestScale *= 2.0f; } - bool result = distanceToCamera <= visibleDistanceAtClosestScale; - - /* - qDebug() << "LODManager::shouldRender() bounds:" << bounds << "result:" << result - << "distanceToCamera:" << distanceToCamera << "visibleDistanceAtClosestScale:" << visibleDistanceAtClosestScale; - */ - - return result; + return distanceToCamera <= visibleDistanceAtClosestScale; }; // TODO: This is essentially the same logic used to render octree cells, but since models are more detailed then octree cells diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 1db666a512..ed51273f88 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -230,27 +230,18 @@ public: class CullItems { public: - CullItems() { - qDebug() << "CullItems() created... "; - } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); typedef Job::ModelIO JobModel; }; class CullItemsOpaque { public: - CullItemsOpaque() { - qDebug() << "CullItemsOpaque() created... "; - } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); typedef Job::ModelIO JobModel; }; class CullItemsTransparent { public: - CullItemsTransparent() { - qDebug() << "CullItemsTransparent() created... "; - } void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); typedef Job::ModelIO JobModel; }; From 6138775270c96e7c5e32f7b71a8b9a275a0f9b22 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 15:40:14 -0700 Subject: [PATCH 0239/1003] put back support for spatialKey --- examples/controllers/handControllerGrab.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 01c02840cb..78f648f445 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -473,12 +473,23 @@ function MyController(hand, triggerAction) { var handRotation = this.getHandRotation(); var handPosition = this.getHandPosition(); - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + if (grabbableData.spatialKey) { + if (grabbableData.spatialKey.relativePosition) { + this.offsetPosition = grabbableData.spatialKey.relativePosition; + } + if (grabbableData.spatialKey.relativeRotation) { + this.offsetRotation = grabbableData.spatialKey.relativeRotation; + } + } else { + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } this.actionID = NULL_ACTION_ID; this.actionID = Entities.addAction("hold", this.grabbedEntity, { From 3716800b98911235074833134eed22fd8939544f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 14 Oct 2015 18:16:13 -0700 Subject: [PATCH 0240/1003] Moved AnimPose class into it's own set of files --- libraries/animation/src/AnimPose.cpp | 56 ++++++++++++++++++++++++ libraries/animation/src/AnimPose.h | 47 ++++++++++++++++++++ libraries/animation/src/AnimSkeleton.cpp | 44 ------------------- libraries/animation/src/AnimSkeleton.h | 28 +----------- 4 files changed, 104 insertions(+), 71 deletions(-) create mode 100644 libraries/animation/src/AnimPose.cpp create mode 100644 libraries/animation/src/AnimPose.h diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp new file mode 100644 index 0000000000..bae34509ae --- /dev/null +++ b/libraries/animation/src/AnimPose.cpp @@ -0,0 +1,56 @@ +// +// AnimPose.cpp +// +// Created by Anthony J. Thibault on 10/14/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 "AnimPose.h" +#include "GLMHelpers.h" + +const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), + glm::quat(), + glm::vec3(0.0f)); + +AnimPose::AnimPose(const glm::mat4& mat) { + scale = extractScale(mat); + rot = glm::normalize(glm::quat_cast(mat)); + trans = extractTranslation(mat); +} + +glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { + return trans + (rot * (scale * rhs)); +} + +glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { + return *this * rhs; +} + +// really slow +glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const { + glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); + glm::mat3 mat(xAxis, yAxis, zAxis); + glm::mat3 transInvMat = glm::inverse(glm::transpose(mat)); + return transInvMat * rhs; +} + +AnimPose AnimPose::operator*(const AnimPose& rhs) const { + return AnimPose(static_cast(*this) * static_cast(rhs)); +} + +AnimPose AnimPose::inverse() const { + return AnimPose(glm::inverse(static_cast(*this))); +} + +AnimPose::operator glm::mat4() const { + glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); + glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); + glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); + return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), + glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); +} diff --git a/libraries/animation/src/AnimPose.h b/libraries/animation/src/AnimPose.h new file mode 100644 index 0000000000..852d84ec1b --- /dev/null +++ b/libraries/animation/src/AnimPose.h @@ -0,0 +1,47 @@ +// +// AnimPose.h +// +// Created by Anthony J. Thibault on 10/14/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 +// + +#ifndef hifi_AnimPose +#define hifi_AnimPose + +#include +#include +#include +#include +#include + +struct AnimPose { + AnimPose() {} + explicit AnimPose(const glm::mat4& mat); + AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} + static const AnimPose identity; + + glm::vec3 xformPoint(const glm::vec3& rhs) const; + glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow + + glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint + AnimPose operator*(const AnimPose& rhs) const; + + AnimPose inverse() const; + operator glm::mat4() const; + + glm::vec3 scale; + glm::quat rot; + glm::vec3 trans; +}; + +inline QDebug operator<<(QDebug debug, const AnimPose& pose) { + debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")"; + return debug; +} + +using AnimPoseVec = std::vector; + +#endif diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index e43a55150c..0db7473c9c 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -16,50 +16,6 @@ #include "AnimationLogging.h" -const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), - glm::quat(), - glm::vec3(0.0f)); - -AnimPose::AnimPose(const glm::mat4& mat) { - scale = extractScale(mat); - rot = glm::normalize(glm::quat_cast(mat)); - trans = extractTranslation(mat); -} - -glm::vec3 AnimPose::operator*(const glm::vec3& rhs) const { - return trans + (rot * (scale * rhs)); -} - -glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const { - return *this * rhs; -} - -// really slow -glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const { - glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); - glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); - glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); - glm::mat3 mat(xAxis, yAxis, zAxis); - glm::mat3 transInvMat = glm::inverse(glm::transpose(mat)); - return transInvMat * rhs; -} - -AnimPose AnimPose::operator*(const AnimPose& rhs) const { - return AnimPose(static_cast(*this) * static_cast(rhs)); -} - -AnimPose AnimPose::inverse() const { - return AnimPose(glm::inverse(static_cast(*this))); -} - -AnimPose::operator glm::mat4() const { - glm::vec3 xAxis = rot * glm::vec3(scale.x, 0.0f, 0.0f); - glm::vec3 yAxis = rot * glm::vec3(0.0f, scale.y, 0.0f); - glm::vec3 zAxis = rot * glm::vec3(0.0f, 0.0f, scale.z); - return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), - glm::vec4(zAxis, 0.0f), glm::vec4(trans, 1.0f)); -} - AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { // convert to std::vector of joints std::vector joints; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index d59719df73..9dda313528 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -16,33 +16,7 @@ #include #include - -struct AnimPose { - AnimPose() {} - explicit AnimPose(const glm::mat4& mat); - AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : scale(scaleIn), rot(rotIn), trans(transIn) {} - static const AnimPose identity; - - glm::vec3 xformPoint(const glm::vec3& rhs) const; - glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow - - glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint - AnimPose operator*(const AnimPose& rhs) const; - - AnimPose inverse() const; - operator glm::mat4() const; - - glm::vec3 scale; - glm::quat rot; - glm::vec3 trans; -}; - -inline QDebug operator<<(QDebug debug, const AnimPose& pose) { - debug << "AnimPose, trans = (" << pose.trans.x << pose.trans.y << pose.trans.z << "), rot = (" << pose.rot.x << pose.rot.y << pose.rot.z << pose.rot.w << "), scale = (" << pose.scale.x << pose.scale.y << pose.scale.z << ")"; - return debug; -} - -using AnimPoseVec = std::vector; +#include "AnimPose.h" class AnimSkeleton { public: From 2b4788929f6cb7e8c5b5944e69415a0b9d73ac42 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 15 Oct 2015 17:57:06 -0700 Subject: [PATCH 0241/1003] AnimBlendLinear: Untested implementation of sync flag. Move accumulateTime into AnimUtil. --- .../defaultAvatar_full/avatar-animation.json | 2 + libraries/animation/src/AnimBlendLinear.cpp | 86 ++++++++++++++++--- libraries/animation/src/AnimBlendLinear.h | 15 +++- libraries/animation/src/AnimClip.cpp | 38 +------- libraries/animation/src/AnimClip.h | 4 +- libraries/animation/src/AnimNode.h | 4 +- libraries/animation/src/AnimNodeLoader.cpp | 3 +- libraries/animation/src/AnimUtil.cpp | 39 +++++++++ libraries/animation/src/AnimUtil.h | 6 +- 9 files changed, 141 insertions(+), 56 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 682e0be1bf..5b042a09b1 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -190,6 +190,7 @@ "type": "blendLinear", "data": { "alpha": 0.0, + "sync": false, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -339,6 +340,7 @@ "type": "blendLinear", "data": { "alpha": 0.0, + "sync": false, "alphaVar": "leftHandGrabBlend" }, "children": [ diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index bc95565f6f..1e65ba2b36 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -12,10 +12,12 @@ #include "GLMHelpers.h" #include "AnimationLogging.h" #include "AnimUtil.h" +#include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync) : AnimNode(AnimNode::Type::BlendLinear, id), - _alpha(alpha) { + _alpha(alpha), + _sync(sync) { } @@ -34,24 +36,19 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo } else if (_children.size() == 1) { _poses = _children[0]->evaluate(animVars, dt, triggersOut); } else { + float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); size_t prevPoseIndex = glm::floor(clampedAlpha); size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - if (prevPoseIndex == nextPoseIndex) { - // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); - } else { - // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); - if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { - _poses.resize(prevPoses.size()); - - ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); - } + float prevPoseDeltaTime = dt; + float nextPoseDeltaTime = dt; + if (_sync) { + setSyncFrameAndComputeDeltaTime(dt, prevPoseIndex, nextPoseIndex, &prevPoseDeltaTime, &nextPoseDeltaTime, triggersOut); } + + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevPoseDeltaTime, nextPoseDeltaTime); } return _poses; } @@ -60,3 +57,64 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { return _poses; } + +void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevPoseDeltaTime, float nextPoseDeltaTime) { + if (prevPoseIndex == nextPoseIndex) { + // this can happen if alpha is on an integer boundary + _poses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + } else { + // need to eval and blend between two children. + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextPoseDeltaTime, triggersOut); + + if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { + _poses.resize(prevPoses.size()); + + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } + } +} + +void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, + float* prevPoseDeltaTime, float* nextPoseDeltaTime, + Triggers& triggersOut) { + std::vector offsets(_children.size(), 0.0f); + std::vector timeScales(_children.size(), 1.0f); + + float lengthSum = 0.0f; + for (size_t i = 0; i < _children.size(); i++) { + // abort if we find a child that is NOT a clipNode. + if (_children[i]->getType() != AnimNode::Type::Clip) { + // TODO: FIXME: make sync this work for other node types. + *prevPoseDeltaTime = dt; + *nextPoseDeltaTime = dt; + return; + } + auto clipNode = std::dynamic_pointer_cast(_children[i]); + assert(clipNode); + if (clipNode) { + lengthSum += clipNode->getEndFrame() - clipNode->getStartFrame(); + } + } + + float averageLength = lengthSum / (float)_children.size(); + + auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); + float prevTimeScale = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) / averageLength; + float prevOffset = prevClipNode->getStartFrame(); + prevClipNode->setCurrentFrame(prevOffset + prevTimeScale / _syncFrame); + + auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); + float nextTimeScale = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) / averageLength; + float nextOffset = nextClipNode->getStartFrame(); + nextClipNode->setCurrentFrame(nextOffset + nextTimeScale / _syncFrame); + + const bool LOOP_FLAG = true; + _syncFrame = ::accumulateTime(0.0f, averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); + + *prevPoseDeltaTime = prevTimeScale; + *nextPoseDeltaTime = nextTimeScale; +} + diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 56acd5c2f7..d8b4f52fb6 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -22,12 +22,15 @@ // between 0 and n - 1. This alpha can be used to linearly interpolate between // the closest two children poses. This can be used to sweep through a series // of animation poses. +// +// The sync flag is used to synchronize between child animations of different lengths. +// Typically used to synchronize blending between walk and run cycles. class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha); + AnimBlendLinear(const QString& id, float alpha, bool sync); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; @@ -38,9 +41,19 @@ protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevPoseDeltaTime, float nextPoseDeltaTime); + void setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, + float* prevPoseDeltaTime, float* nextPoseDeltaTime, + Triggers& triggersOut); + AnimPoseVec _poses; float _alpha; + bool _sync; + float _syncFrame = 0.0f; + float _timeScale = 1.0f; // TODO: HOOK THIS UP TO AN ANIMVAR. QString _alphaVar; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 251cb0047a..97f46a1b68 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -35,7 +35,9 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, _endFrame = animVars.lookup(_endFrameVar, _endFrame); _timeScale = animVars.lookup(_timeScaleVar, _timeScale); _loopFlag = animVars.lookup(_loopFlagVar, _loopFlag); - _frame = accumulateTime(animVars.lookup(_frameVar, _frame), dt, triggersOut); + float frame = animVars.lookup(_frameVar, _frame); + + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggersOut); // poll network anim to see if it's finished loading yet. if (_networkAnim && _networkAnim->isLoaded() && _skeleton) { @@ -78,39 +80,7 @@ void AnimClip::setCurrentFrameInternal(float frame) { // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _frame = accumulateTime(frame * _timeScale, dt, triggers); -} - -float AnimClip::accumulateTime(float frame, float dt, Triggers& triggersOut) const { - const float startFrame = std::min(_startFrame, _endFrame); - if (startFrame == _endFrame) { - // when startFrame >= endFrame - frame = _endFrame; - } else if (_timeScale > 0.0f) { - // accumulate time, keeping track of loops and end of animation events. - const float FRAMES_PER_SECOND = 30.0f; - float framesRemaining = (dt * _timeScale) * FRAMES_PER_SECOND; - while (framesRemaining > 0.0f) { - float framesTillEnd = _endFrame - _frame; - if (framesRemaining >= framesTillEnd) { - if (_loopFlag) { - // anim loop - triggersOut.push_back(_id + "OnLoop"); - framesRemaining -= framesTillEnd; - frame = startFrame; - } else { - // anim end - triggersOut.push_back(_id + "OnDone"); - frame = _endFrame; - framesRemaining = 0.0f; - } - } else { - frame += framesRemaining; - framesRemaining = 0.0f; - } - } - } - return frame; + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame * _timeScale, dt, _loopFlag, _id, triggers); } void AnimClip::copyFromNetworkAnim() { diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 3a76870c98..b7cd2861fa 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -36,12 +36,14 @@ public: void setLoopFlagVar(const QString& loopFlagVar) { _loopFlagVar = loopFlagVar; } void setFrameVar(const QString& frameVar) { _frameVar = frameVar; } + float getStartFrame() const { return _startFrame; } + float getEndFrame() const { return _endFrame; } + protected: void loadURL(const QString& url); virtual void setCurrentFrameInternal(float frame) override; - float accumulateTime(float frame, float dt, Triggers& triggersOut) const; void copyFromNetworkAnim(); // for AnimDebugDraw rendering diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 9a21526409..315ee357dd 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -75,10 +75,10 @@ public: return evaluate(animVars, dt, triggersOut); } -protected: - void setCurrentFrame(float frame); +protected: + virtual void setCurrentFrameInternal(float frame) {} virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) { _skeleton = skeleton; } diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 147025f1cf..2acbeed734 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -221,10 +221,11 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); + READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); - auto node = std::make_shared(id, alpha); + auto node = std::make_shared(id, alpha, sync); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index 81b294e66c..c8efa85df9 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -11,6 +11,9 @@ #include "AnimUtil.h" #include "GLMHelpers.h" +// TODO: use restrict keyword +// TODO: excellent candidate for simd vectorization. + void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result) { for (size_t i = 0; i < numPoses; i++) { const AnimPose& aPose = a[i]; @@ -20,3 +23,39 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A result[i].trans = lerp(aPose.trans, bPose.trans, alpha); } } + +float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag, + const QString& id, AnimNode::Triggers& triggersOut) { + + float frame = currentFrame; + const float clampedStartFrame = std::min(startFrame, endFrame); + if (clampedStartFrame == endFrame) { + // when clampedStartFrame >= endFrame + frame = endFrame; + } else if (timeScale > 0.0f) { + // accumulate time, keeping track of loops and end of animation events. + const float FRAMES_PER_SECOND = 30.0f; + float framesRemaining = (dt * timeScale) * FRAMES_PER_SECOND; + while (framesRemaining > 0.0f) { + float framesTillEnd = endFrame - frame; + if (framesRemaining >= framesTillEnd) { + if (loopFlag) { + // anim loop + triggersOut.push_back(id + "OnLoop"); + framesRemaining -= framesTillEnd; + frame = clampedStartFrame; + } else { + // anim end + triggersOut.push_back(id + "OnDone"); + frame = endFrame; + framesRemaining = 0.0f; + } + } else { + frame += framesRemaining; + framesRemaining = 0.0f; + } + } + } + return frame; +} + diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index 23c02b6183..6d394be882 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -13,12 +13,12 @@ #include "AnimNode.h" -// TODO: use restrict keyword -// TODO: excellent candidate for simd vectorization. - // this is where the magic happens void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, AnimPose* result); +float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag, + const QString& id, AnimNode::Triggers& triggersOut); + #endif From 9b9bd7fe269ce0eb22390ede7554fd3e70f1981e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 09:29:59 -0700 Subject: [PATCH 0242/1003] AnimNodeLoader: Fix for crash condition When a node with children had an error loading, it would lead to a nullptr dereference. --- libraries/animation/src/AnimNodeLoader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 2acbeed734..4222364629 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -160,6 +160,9 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr assert((int)type >= 0 && type < AnimNode::Type::NumTypes); auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl); + if (!node) { + return nullptr; + } auto childrenValue = jsonObj.value("children"); if (!childrenValue.isArray()) { From 8e7e94c50102e554efb5c7da532690d1bcfd79a9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 09:31:14 -0700 Subject: [PATCH 0243/1003] AnimTests: now compile and pass again. --- tests/animation/src/AnimTests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index a08288000e..200a6c0c3c 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -30,8 +30,8 @@ void AnimTests::cleanupTestCase() { } void AnimTests::testClipInternalState() { - std::string id = "my anim clip"; - std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + QString id = "my anim clip"; + QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 20.0f; float timeScale = 1.1f; @@ -55,8 +55,8 @@ static float framesToSec(float secs) { } void AnimTests::testClipEvaulate() { - std::string id = "myClipNode"; - std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + QString id = "myClipNode"; + QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; float timeScale = 1.0f; @@ -90,8 +90,8 @@ void AnimTests::testClipEvaulate() { } void AnimTests::testClipEvaulateWithVars() { - std::string id = "myClipNode"; - std::string url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; + QString id = "myClipNode"; + QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx"; float startFrame = 2.0f; float endFrame = 22.0f; float timeScale = 1.0f; @@ -126,9 +126,9 @@ void AnimTests::testClipEvaulateWithVars() { } void AnimTests::testLoader() { - auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/8ba57a8f0a76f88b39a11f77f8d9df04af9cec95/test.json"); + auto url = QUrl("https://gist.githubusercontent.com/hyperlogic/857129fe04567cbe670f/raw/0c54500f480fd7314a5aeb147c45a8a707edcc2e/test.json"); // NOTE: This will warn about missing "test01.fbx", "test02.fbx", etc. if the resource loading code doesn't handle relative pathnames! - // However, the test will proceed. + // However, the test will proceed. AnimNodeLoader loader(url); const int timeout = 1000; From 073cec41c41c77546824f2fda5780c8a28c86df5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 12:11:46 -0700 Subject: [PATCH 0244/1003] AnimClip & accumulateTime smoother looping anims Looping animations should have an extra frame of interpolation between the start and end frames. --- libraries/animation/src/AnimClip.cpp | 11 ++++++----- libraries/animation/src/AnimUtil.cpp | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 97f46a1b68..613194164f 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -47,16 +47,17 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, } if (_anim.size()) { - int frameCount = _anim.size(); - int prevIndex = (int)glm::floor(_frame); - int nextIndex = (int)glm::ceil(_frame); - if (_loopFlag && nextIndex >= frameCount) { - nextIndex = 0; + int nextIndex; + if (_loopFlag && _frame >= _endFrame) { + nextIndex = (int)glm::ceil(_startFrame); + } else { + nextIndex = (int)glm::ceil(_frame); } // It can be quite possible for the user to set _startFrame and _endFrame to // values before or past valid ranges. We clamp the frames here. + int frameCount = _anim.size(); prevIndex = std::min(std::max(0, prevIndex), frameCount - 1); nextIndex = std::min(std::max(0, nextIndex), frameCount - 1); diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index c8efa85df9..e9e5ea95de 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -29,8 +29,7 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu float frame = currentFrame; const float clampedStartFrame = std::min(startFrame, endFrame); - if (clampedStartFrame == endFrame) { - // when clampedStartFrame >= endFrame + if (fabsf(clampedStartFrame - endFrame) < 1.0f) { frame = endFrame; } else if (timeScale > 0.0f) { // accumulate time, keeping track of loops and end of animation events. @@ -38,6 +37,10 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu float framesRemaining = (dt * timeScale) * FRAMES_PER_SECOND; while (framesRemaining > 0.0f) { float framesTillEnd = endFrame - frame; + // when looping, add one frame between start and end. + if (loopFlag) { + framesTillEnd += 1.0f; + } if (framesRemaining >= framesTillEnd) { if (loopFlag) { // anim loop From 15f3894001086b2ad856c242079f9d0f52887d96 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 12:13:31 -0700 Subject: [PATCH 0245/1003] AnimTests: added tests for accumulateTime --- tests/animation/src/AnimTests.cpp | 89 ++++++++++++++++++++++++++++++- tests/animation/src/AnimTests.h | 3 ++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/tests/animation/src/AnimTests.cpp b/tests/animation/src/AnimTests.cpp index 200a6c0c3c..1b5bb4739a 100644 --- a/tests/animation/src/AnimTests.cpp +++ b/tests/animation/src/AnimTests.cpp @@ -13,6 +13,7 @@ #include "AnimBlendLinear.h" #include "AnimationLogging.h" #include "AnimVariant.h" +#include "AnimUtil.h" #include <../QTestExtensions.h> @@ -73,8 +74,8 @@ void AnimTests::testClipEvaulate() { // does it loop? triggers.clear(); - clip.evaluate(vars, framesToSec(11.0f), triggers); - QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); + clip.evaluate(vars, framesToSec(12.0f), triggers); + QCOMPARE_WITH_ABS_ERROR(clip._frame, 3.0f, EPSILON); // Note: frame 3 and not 4, because extra frame between start and end. // did we receive a loop trigger? QVERIFY(std::find(triggers.begin(), triggers.end(), "myClipNodeOnLoop") != triggers.end()); @@ -238,3 +239,87 @@ void AnimTests::testVariant() { QVERIFY(m[1].z == -7.0f); QVERIFY(m[3].w == 16.0f); } + +void AnimTests::testAccumulateTime() { + + float startFrame = 0.0f; + float endFrame = 10.0f; + float timeScale = 1.0f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); + + startFrame = 5.0f; + endFrame = 15.0f; + timeScale = 1.0f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); + + startFrame = 0.0f; + endFrame = 10.0f; + timeScale = 0.5f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); + + startFrame = 5.0f; + endFrame = 15.0f; + timeScale = 2.0f; + testAccumulateTimeWithParameters(startFrame, endFrame, timeScale); +} + +void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const { + + float dt = (1.0f / 30.0f) / timeScale; // sec + QString id = "testNode"; + AnimNode::Triggers triggers; + bool loopFlag = false; + + float resultFrame = accumulateTime(startFrame, endFrame, timeScale, startFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 1.0f); + QVERIFY(triggers.empty()); + triggers.clear(); + + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 2.0f); + QVERIFY(triggers.empty()); + triggers.clear(); + + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 3.0f); + QVERIFY(triggers.empty()); + triggers.clear(); + + // test onDone trigger and frame clamping. + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); + triggers.clear(); + + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnDone"); + triggers.clear(); + + // test onLoop trigger and looping frame logic + loopFlag = true; + + // should NOT trigger loop even though we stop at last frame, because there is an extra frame between end and start frames. + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 1.0f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame); + QVERIFY(triggers.empty()); + triggers.clear(); + + // now we should hit loop trigger + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); + triggers.clear(); + + // should NOT trigger loop, even though we move past the end frame, because of extra frame between end and start. + resultFrame = accumulateTime(startFrame, endFrame, timeScale, endFrame - 0.5f, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == endFrame + 0.5f); + QVERIFY(triggers.empty()); + triggers.clear(); + + // now we should hit loop trigger + resultFrame = accumulateTime(startFrame, endFrame, timeScale, resultFrame, dt, loopFlag, id, triggers); + QVERIFY(resultFrame == startFrame + 0.5f); + QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop"); + triggers.clear(); +} diff --git a/tests/animation/src/AnimTests.h b/tests/animation/src/AnimTests.h index e667444657..7bd05369c7 100644 --- a/tests/animation/src/AnimTests.h +++ b/tests/animation/src/AnimTests.h @@ -15,6 +15,8 @@ class AnimTests : public QObject { Q_OBJECT +public: + void testAccumulateTimeWithParameters(float startFrame, float endFrame, float timeScale) const; private slots: void initTestCase(); void cleanupTestCase(); @@ -23,6 +25,7 @@ private slots: void testClipEvaulateWithVars(); void testLoader(); void testVariant(); + void testAccumulateTime(); }; #endif // hifi_AnimTests_h From 0c36180e2fd8acefb4c49379ef88bcdf3eac3781 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 16 Oct 2015 12:17:01 -0700 Subject: [PATCH 0246/1003] avatar-animation.json: Adjustment to sidestep endFrame, to prevent stalling at end --- .../resources/meshes/defaultAvatar_full/avatar-animation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 5b042a09b1..be1abbc820 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -579,7 +579,7 @@ "data": { "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", "startFrame": 0.0, - "endFrame": 31.0, + "endFrame": 30.0, "timeScale": 1.0, "loopFlag": true }, @@ -591,7 +591,7 @@ "data": { "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", "startFrame": 0.0, - "endFrame": 31.0, + "endFrame": 30.0, "timeScale": 1.0, "loopFlag": true }, From a1cfebc173695dbff90c97c5f356ac5a6922727b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 15:57:15 -0700 Subject: [PATCH 0247/1003] don't accept incoming location edits if an entity is being controlled by a shouldSuppressLocationEdits action --- libraries/entities/src/EntityTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6e487cbfe3..8449ce050b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -189,7 +189,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI } else { simulationBlocked = senderID != entity->getSimulatorID(); } - if (simulationBlocked) { + if (simulationBlocked || entity->shouldSuppressLocationEdits()) { // squash ownership and physics-related changes. properties.setSimulationOwnerChanged(false); properties.setPositionChanged(false); From 00594f0ccea027ffa6051cacedc231ee650ead1b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 16:05:10 -0700 Subject: [PATCH 0248/1003] don't accept incoming location edits if an entity is being controlled by a shouldSuppressLocationEdits action --- libraries/entities/src/EntityItem.cpp | 24 ++++++++++++++++++++++++ libraries/entities/src/EntityItem.h | 8 ++++---- libraries/entities/src/EntityTree.cpp | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9b3821b56a..3eb8a84bca 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1808,3 +1808,27 @@ bool EntityItem::shouldSuppressLocationEdits() const { return false; } + +void EntityItem::setPosition(const glm::vec3& value) { + if (!entity->shouldSuppressLocationEdits()) { + _transform.setTranslation(value); requiresRecalcBoxes(); + } +} + +void EntityItem::setRotation(const glm::quat& rotation) { + if (!entity->shouldSuppressLocationEdits()) { + _transform.setRotation(rotation); requiresRecalcBoxes(); + } +} + +void EntityItem::setVelocity(const glm::vec3& value) { + if (!entity->shouldSuppressLocationEdits()) { + _velocity = value; + } +} + +void EntityItem::setAcceleration(const glm::vec3& value) { + if (!entity->shouldSuppressLocationEdits()) { + _acceleration = value; + } +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 858dc7e326..172ff79276 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -209,10 +209,10 @@ public: /// Position in meters (-TREE_SCALE - TREE_SCALE) inline const glm::vec3& getPosition() const { return _transform.getTranslation(); } - inline void setPosition(const glm::vec3& value) { _transform.setTranslation(value); requiresRecalcBoxes(); } + void setPosition(const glm::vec3& value); inline const glm::quat& getRotation() const { return _transform.getRotation(); } - inline void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); requiresRecalcBoxes(); } + inline void setRotation(const glm::quat& rotation); inline void requiresRecalcBoxes() { _recalcAABox = true; _recalcMinAACube = true; _recalcMaxAACube = true; } @@ -240,7 +240,7 @@ public: float getDensity() const { return _density; } const glm::vec3& getVelocity() const { return _velocity; } /// get velocity in meters - void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters + void setVelocity(const glm::vec3& value); bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters @@ -248,7 +248,7 @@ public: bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; } const glm::vec3& getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second - void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second + void setAcceleration(const glm::vec3& value); /// acceleration in meters/second/second bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; } float getDamping() const { return _damping; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8449ce050b..6e487cbfe3 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -189,7 +189,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI } else { simulationBlocked = senderID != entity->getSimulatorID(); } - if (simulationBlocked || entity->shouldSuppressLocationEdits()) { + if (simulationBlocked) { // squash ownership and physics-related changes. properties.setSimulationOwnerChanged(false); properties.setPositionChanged(false); From 9e24542c0b1e89913072b39ed0b3f2a42816eefc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 16:05:56 -0700 Subject: [PATCH 0249/1003] don't accept incoming location edits if an entity is being controlled by a shouldSuppressLocationEdits action --- libraries/entities/src/EntityItem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3eb8a84bca..00ba6e8d42 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1810,25 +1810,25 @@ bool EntityItem::shouldSuppressLocationEdits() const { } void EntityItem::setPosition(const glm::vec3& value) { - if (!entity->shouldSuppressLocationEdits()) { + if (!shouldSuppressLocationEdits()) { _transform.setTranslation(value); requiresRecalcBoxes(); } } void EntityItem::setRotation(const glm::quat& rotation) { - if (!entity->shouldSuppressLocationEdits()) { + if (!shouldSuppressLocationEdits()) { _transform.setRotation(rotation); requiresRecalcBoxes(); } } void EntityItem::setVelocity(const glm::vec3& value) { - if (!entity->shouldSuppressLocationEdits()) { + if (!shouldSuppressLocationEdits()) { _velocity = value; } } void EntityItem::setAcceleration(const glm::vec3& value) { - if (!entity->shouldSuppressLocationEdits()) { + if (!shouldSuppressLocationEdits()) { _acceleration = value; } } From bddbe89c865b228b43496f0c73942cb00d93e661 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 16:13:53 -0700 Subject: [PATCH 0250/1003] don't accept incoming location edits if an entity is being controlled by a shouldSuppressLocationEdits action --- libraries/entities/src/EntityItem.cpp | 36 +++++++++------------------ libraries/entities/src/EntityItem.h | 8 +++--- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 00ba6e8d42..1fd9acc1e2 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1301,6 +1301,9 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) { } void EntityItem::updatePosition(const glm::vec3& value) { + if (shouldSuppressLocationEdits()) { + return; + } auto delta = glm::distance(getPosition(), value); if (delta > IGNORE_POSITION_DELTA) { _dirtyFlags |= Simulation::DIRTY_POSITION; @@ -1323,6 +1326,9 @@ void EntityItem::updateDimensions(const glm::vec3& value) { } void EntityItem::updateRotation(const glm::quat& rotation) { + if (shouldSuppressLocationEdits()) { + return; + } if (getRotation() != rotation) { setRotation(rotation); @@ -1363,6 +1369,9 @@ void EntityItem::updateMass(float mass) { } void EntityItem::updateVelocity(const glm::vec3& value) { + if (shouldSuppressLocationEdits()) { + return; + } auto delta = glm::distance(_velocity, value); if (delta > IGNORE_LINEAR_VELOCITY_DELTA) { _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; @@ -1399,6 +1408,9 @@ void EntityItem::updateGravity(const glm::vec3& value) { } void EntityItem::updateAngularVelocity(const glm::vec3& value) { + if (shouldSuppressLocationEdits()) { + return; + } auto delta = glm::distance(_angularVelocity, value); if (delta > IGNORE_ANGULAR_VELOCITY_DELTA) { _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY; @@ -1808,27 +1820,3 @@ bool EntityItem::shouldSuppressLocationEdits() const { return false; } - -void EntityItem::setPosition(const glm::vec3& value) { - if (!shouldSuppressLocationEdits()) { - _transform.setTranslation(value); requiresRecalcBoxes(); - } -} - -void EntityItem::setRotation(const glm::quat& rotation) { - if (!shouldSuppressLocationEdits()) { - _transform.setRotation(rotation); requiresRecalcBoxes(); - } -} - -void EntityItem::setVelocity(const glm::vec3& value) { - if (!shouldSuppressLocationEdits()) { - _velocity = value; - } -} - -void EntityItem::setAcceleration(const glm::vec3& value) { - if (!shouldSuppressLocationEdits()) { - _acceleration = value; - } -} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 172ff79276..858dc7e326 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -209,10 +209,10 @@ public: /// Position in meters (-TREE_SCALE - TREE_SCALE) inline const glm::vec3& getPosition() const { return _transform.getTranslation(); } - void setPosition(const glm::vec3& value); + inline void setPosition(const glm::vec3& value) { _transform.setTranslation(value); requiresRecalcBoxes(); } inline const glm::quat& getRotation() const { return _transform.getRotation(); } - inline void setRotation(const glm::quat& rotation); + inline void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); requiresRecalcBoxes(); } inline void requiresRecalcBoxes() { _recalcAABox = true; _recalcMinAACube = true; _recalcMaxAACube = true; } @@ -240,7 +240,7 @@ public: float getDensity() const { return _density; } const glm::vec3& getVelocity() const { return _velocity; } /// get velocity in meters - void setVelocity(const glm::vec3& value); + void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; } const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters @@ -248,7 +248,7 @@ public: bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; } const glm::vec3& getAcceleration() const { return _acceleration; } /// get acceleration in meters/second/second - void setAcceleration(const glm::vec3& value); /// acceleration in meters/second/second + void setAcceleration(const glm::vec3& value) { _acceleration = value; } /// acceleration in meters/second/second bool hasAcceleration() const { return _acceleration != ENTITY_ITEM_ZERO_VEC3; } float getDamping() const { return _damping; } From 5a0bb4c31bd7717b959aae9d2219a74bc8ba0d93 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 16:51:31 -0700 Subject: [PATCH 0251/1003] 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 41f7ef1e2e52bff69c0c9879560bfa13dfe9be85 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 16:53:27 -0700 Subject: [PATCH 0252/1003] cleanups --- examples/controllers/handControllerGrab.js | 6 ++---- interface/src/avatar/AvatarActionHold.cpp | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 78f648f445..1e77125d00 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -497,8 +497,7 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - lifetime: ACTION_LIFETIME, - // kinematic: true, + lifetime: ACTION_LIFETIME }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -550,8 +549,7 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - lifetime: ACTION_LIFETIME, - // kinematic: true + lifetime: ACTION_LIFETIME }); this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 4d07ecb5e3..cf455ea98c 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -175,7 +175,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { ok = true; kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments, - "kinematic-set-velocity", ok, false); + "kinematicSetVelocity", ok, false); if (!ok) { _kinematicSetVelocity = false; } From 7669f9ed2c82d26b7231de7b861099fa22a8212d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 17:26:57 -0700 Subject: [PATCH 0253/1003] 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 0254/1003] 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 0255/1003] 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 4feb9dc8c60a6df7eb01e3ff437cc3ba658941ed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 19 Oct 2015 17:46:34 -0700 Subject: [PATCH 0256/1003] prepare for possible future coding standard --- interface/src/avatar/AvatarActionHold.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 042e6a0f25..6badf97e9e 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -41,9 +41,9 @@ private: QUuid _holderID; void doKinematicUpdate(float deltaTimeStep); - bool _kinematic = false; - bool _kinematicSetVelocity = false; - bool _previousSet = false; + bool _kinematic { false }; + bool _kinematicSetVelocity { false }; + bool _previousSet { false }; glm::vec3 _previousPositionalTarget; glm::quat _previousRotationalTarget; }; From 9f17f10d6af4a623d5bcbfd3b4291dde474ad5fc Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 19 Oct 2015 17:56:38 -0700 Subject: [PATCH 0257/1003] fix warning in master --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 008694717f..a393ca5316 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -369,7 +369,7 @@ Menu::Menu() { auto& atpMigrator = ATPAssetMigrator::getInstance(); atpMigrator.setDialogParent(this); - QAction* assetMigration = addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, + addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration, 0, &atpMigrator, SLOT(loadEntityServerFile())); From 1c46f2aa7578cb9d031f061c0f775a7b74799cc1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 19 Oct 2015 18:22:44 -0700 Subject: [PATCH 0258/1003] Fix autoresizing log spam --- libraries/entities-renderer/src/RenderableModelEntityItem.h | 2 +- libraries/entities/src/EntityItemProperties.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index c87f70d4d6..4dc1cced48 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -79,7 +79,7 @@ private: QStringList _originalTextures; bool _originalTexturesRead = false; QVector> _points; - bool _dimensionsInitialized = false; + bool _dimensionsInitialized = true; render::ItemID _myMetaItem; }; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 15c6669876..2227644484 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -270,7 +270,7 @@ private: bool _glowLevelChanged; bool _localRenderAlphaChanged; bool _defaultSettings; - bool _dimensionsInitialized = false; // Only true if creating an entity localy with no dimensions properties + bool _dimensionsInitialized = true; // Only false if creating an entity localy with no dimensions properties // NOTE: The following are pseudo client only properties. They are only used in clients which can access // properties of model geometry. But these properties are not serialized like other properties. From 8701d73ee957f39c6d8e27e98b9a6b47198ac545 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 19 Oct 2015 19:05:37 -0700 Subject: [PATCH 0259/1003] 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 380b10ab8df9da1438aa18172bec38035f455b4d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 19:32:27 -0700 Subject: [PATCH 0260/1003] Fixing a bug in the gitattributes file --- .gitattributes | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index 5a6cc5e815..bf357796a4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,7 +19,7 @@ *.png binary *.jpg binary *.wav binary -*.fbx text -*.dds text -*.svg text -*.ttf text +*.fbx binary +*.dds binary +*.svg binary +*.ttf binary From 2213a4bb021962ae38aa90f77d2dc0311aed2972 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 19 Oct 2015 20:09:48 -0700 Subject: [PATCH 0261/1003] 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 0373a481f8475f4948373fbefdba406b3ec75957 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 20 Oct 2015 09:22:16 -0700 Subject: [PATCH 0262/1003] Using EntityItemIDs instead of QUuids --- .../entities-renderer/src/EntityTreeRenderer.cpp | 2 +- .../entities-renderer/src/EntityTreeRenderer.h | 2 +- .../entities/src/EntityScriptingInterface.cpp | 6 +++--- .../entities/src/EntityScriptingInterface.h | 2 +- libraries/entities/src/EntityTree.cpp | 4 ++-- libraries/entities/src/EntityTree.h | 2 +- libraries/entities/src/EntityTreeElement.cpp | 4 ++-- libraries/entities/src/EntityTreeElement.h | 4 ++-- libraries/shared/src/RegisteredMetaTypes.cpp | 16 +++++++++++++++- libraries/shared/src/RegisteredMetaTypes.h | 2 ++ 10 files changed, 30 insertions(+), 14 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 8f316af276..d5bf0bde8a 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -482,7 +482,7 @@ void EntityTreeRenderer::deleteReleasedModels() { } RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude) { RayToEntityIntersectionResult result; if (_tree) { EntityTreePointer entityTree = std::static_pointer_cast(_tree); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 15c030f77e..5cd86fba21 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -130,7 +130,7 @@ private: QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude = QVector()); + bool precisionPicking, const QVector& entityIdsToInclude = QVector()); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 07c932b34e..01d46e0a91 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -280,18 +280,18 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - QVector entities = qVectorQUuidFromScriptValue(entityIdsToInclude); + QVector entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - const QVector& entities = qVectorQUuidFromScriptValue(entityIdsToInclude); + const QVector& entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude) { RayToEntityIntersectionResult result; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 48f13e81bd..d764cd7bab 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -186,7 +186,7 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude); + bool precisionPicking, const QVector& entityIdsToInclude); EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e45e3e8a1e..24c13ae28e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -457,7 +457,7 @@ public: float& distance; BoxFace& face; glm::vec3& surfaceNormal; - const QVector& entityIdsToInclude; + const QVector& entityIdsToInclude; void** intersectedObject; bool found; bool precisionPicking; @@ -478,7 +478,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking }; distance = FLT_MAX; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 17c7347b2e..1957787a60 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -82,7 +82,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToInclude = QVector(), void** intersectedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL, diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index b3571097b3..8b3721488c 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -495,7 +495,7 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking) { keepSearching = true; // assume that we will continue searching after this. @@ -536,7 +536,7 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { + const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... int entityNumber = 0; diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 78537dca3f..3be52a3740 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -144,11 +144,11 @@ public: virtual bool canRayIntersect() const { return hasEntities(); } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 9ab0eaecb4..016c99a8dc 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -13,7 +13,6 @@ #include #include #include - #include #include "RegisteredMetaTypes.h" @@ -119,6 +118,21 @@ 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 cd1e3b0d3e..8e53f0ee37 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -14,6 +14,7 @@ #include #include +#include "../../entities/src/EntityItemID.h" #include #include @@ -66,6 +67,7 @@ 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 dfe3e5b1cbdedad51fa829202ec22c8144faa30d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Oct 2015 09:30:27 -0700 Subject: [PATCH 0263/1003] fix laser lifetimes --- 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 1e77125d00..d2ad2aa4be 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -64,7 +64,6 @@ var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; var MSEC_PER_SEC = 1000.0; // these control how long an abandoned pointer line will hang around -var startTime = Date.now(); var LIFETIME = 10; var ACTION_LIFETIME = 15; // seconds var ACTION_LIFETIME_REFRESH = 5; @@ -203,11 +202,12 @@ function MyController(hand, triggerAction) { lifetime: LIFETIME }); } else { + var age = Entities.getEntityProperties(this.pointer, "age").age; Entities.editEntity(this.pointer, { position: closePoint, linePoints: [ZERO_VEC, farPoint], color: color, - lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME + lifetime: age + LIFETIME }); } From 7fa069cf303b26a8fe72bddafafdb60c8355fac2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Oct 2015 09:38:25 -0700 Subject: [PATCH 0264/1003] fix line endings grumble gruble --- libraries/gpu/src/gpu/Framebuffer.cpp | 538 +++++------ libraries/gpu/src/gpu/Framebuffer.h | 334 +++---- libraries/gpu/src/gpu/Pipeline.cpp | 52 +- libraries/gpu/src/gpu/State.cpp | 176 ++-- libraries/gpu/src/gpu/Texture.h | 902 +++++++++--------- libraries/gpu/src/gpu/Transform.slh | 286 +++--- libraries/model/src/model/Atmosphere.slh | 490 +++++----- libraries/model/src/model/Light.h | 42 +- libraries/model/src/model/Light.slh | 186 ++-- libraries/model/src/model/Material.slh | 132 +-- libraries/model/src/model/Stage.cpp | 606 ++++++------ libraries/model/src/model/Stage.h | 494 +++++----- libraries/octree/src/Plane.cpp | 142 +-- libraries/octree/src/Plane.h | 92 +- .../src/procedural/ProceduralSkybox.cpp | 126 +-- libraries/render-utils/src/DeferredBuffer.slh | 20 +- 16 files changed, 2309 insertions(+), 2309 deletions(-) diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index d570007b3e..ea650c6ad9 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -7,279 +7,279 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Framebuffer.h" -#include -#include - -using namespace gpu; - -Framebuffer::~Framebuffer() { -} - -Framebuffer* Framebuffer::create() { - auto framebuffer = new Framebuffer(); - framebuffer->_renderBuffers.resize(MAX_NUM_RENDER_BUFFERS); - return framebuffer; -} - - -Framebuffer* Framebuffer::create( const Format& colorBufferFormat, uint16 width, uint16 height) { - auto framebuffer = Framebuffer::create(); - - auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - - framebuffer->setRenderBuffer(0, colorTexture); - - return framebuffer; -} - -Framebuffer* Framebuffer::create( const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) { - auto framebuffer = Framebuffer::create(); - - auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - - framebuffer->setRenderBuffer(0, colorTexture); - framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); - - return framebuffer; -} - -Framebuffer* Framebuffer::createShadowmap(uint16 width) { - auto framebuffer = Framebuffer::create(); - auto depthTexture = TexturePointer(Texture::create2D(Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH), width, width)); +// + +#include "Framebuffer.h" +#include +#include + +using namespace gpu; + +Framebuffer::~Framebuffer() { +} + +Framebuffer* Framebuffer::create() { + auto framebuffer = new Framebuffer(); + framebuffer->_renderBuffers.resize(MAX_NUM_RENDER_BUFFERS); + return framebuffer; +} + + +Framebuffer* Framebuffer::create( const Format& colorBufferFormat, uint16 width, uint16 height) { + auto framebuffer = Framebuffer::create(); + + auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + + framebuffer->setRenderBuffer(0, colorTexture); + + return framebuffer; +} + +Framebuffer* Framebuffer::create( const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) { + auto framebuffer = Framebuffer::create(); + + auto colorTexture = TexturePointer(Texture::create2D(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto depthTexture = TexturePointer(Texture::create2D(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + + framebuffer->setRenderBuffer(0, colorTexture); + framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); + + return framebuffer; +} + +Framebuffer* Framebuffer::createShadowmap(uint16 width) { + auto framebuffer = Framebuffer::create(); + auto depthTexture = TexturePointer(Texture::create2D(Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH), width, width)); Sampler::Desc samplerDesc; samplerDesc._borderColor = glm::vec4(1.0f); samplerDesc._wrapModeU = Sampler::WRAP_BORDER; samplerDesc._wrapModeV = Sampler::WRAP_BORDER; samplerDesc._filter = Sampler::FILTER_MIN_MAG_LINEAR; - samplerDesc._comparisonFunc = LESS_EQUAL; - - depthTexture->setSampler(Sampler(samplerDesc)); - - framebuffer->setDepthStencilBuffer(depthTexture, Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH)); - - return framebuffer; -} - -bool Framebuffer::isSwapchain() const { - return _swapchain != 0; -} - -uint32 Framebuffer::getFrameCount() const { - if (_swapchain) { - return _swapchain->getFrameCount(); - } else { - return _frameCount; - } -} - -bool Framebuffer::validateTargetCompatibility(const Texture& texture, uint32 subresource) const { - if (texture.getType() == Texture::TEX_1D) { - return false; - } - - if (isEmpty()) { - return true; - } else { - if ((texture.getWidth() == getWidth()) && - (texture.getHeight() == getHeight()) && - (texture.getNumSamples() == getNumSamples())) { - return true; - } else { - return false; - } - } -} - -void Framebuffer::updateSize(const TexturePointer& texture) { - if (!isEmpty()) { - return; - } - - if (texture) { - _width = texture->getWidth(); - _height = texture->getHeight(); - _numSamples = texture->getNumSamples(); - } else { - _width = _height = _numSamples = 0; - } -} - -void Framebuffer::resize(uint16 width, uint16 height, uint16 numSamples) { - if (width && height && numSamples && !isEmpty() && !isSwapchain()) { - if ((width != _width) || (height != _height) || (numSamples != _numSamples)) { - for (uint32 i = 0; i < _renderBuffers.size(); ++i) { - if (_renderBuffers[i]) { - _renderBuffers[i]._texture->resize2D(width, height, numSamples); - _numSamples = _renderBuffers[i]._texture->getNumSamples(); - } - } - - if (_depthStencilBuffer) { - _depthStencilBuffer._texture->resize2D(width, height, numSamples); - _numSamples = _depthStencilBuffer._texture->getNumSamples(); - } - - _width = width; - _height = height; - // _numSamples = numSamples; - } - } -} - -uint16 Framebuffer::getWidth() const { - if (isSwapchain()) { - return getSwapchain()->getWidth(); - } else { - return _width; - } -} - -uint16 Framebuffer::getHeight() const { - if (isSwapchain()) { - return getSwapchain()->getHeight(); - } else { - return _height; - } -} - -uint16 Framebuffer::getNumSamples() const { - if (isSwapchain()) { - return getSwapchain()->getNumSamples(); - } else { - return _numSamples; - } -} - -// Render buffers -int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uint32 subresource) { - if (isSwapchain()) { - return -1; - } - - // Check for the slot - if (slot >= getMaxNumRenderBuffers()) { - return -1; - } - - // Check for the compatibility of size - if (texture) { - if (!validateTargetCompatibility(*texture, subresource)) { - return -1; - } - } - - updateSize(texture); - - // assign the new one - _renderBuffers[slot] = TextureView(texture, subresource); - - // update the mask - int mask = (1<getDepthStencilBufferFormat(); - return _depthStencilBuffer._element; - } else { - return _depthStencilBuffer._element; - } + samplerDesc._comparisonFunc = LESS_EQUAL; + + depthTexture->setSampler(Sampler(samplerDesc)); + + framebuffer->setDepthStencilBuffer(depthTexture, Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH)); + + return framebuffer; +} + +bool Framebuffer::isSwapchain() const { + return _swapchain != 0; +} + +uint32 Framebuffer::getFrameCount() const { + if (_swapchain) { + return _swapchain->getFrameCount(); + } else { + return _frameCount; + } +} + +bool Framebuffer::validateTargetCompatibility(const Texture& texture, uint32 subresource) const { + if (texture.getType() == Texture::TEX_1D) { + return false; + } + + if (isEmpty()) { + return true; + } else { + if ((texture.getWidth() == getWidth()) && + (texture.getHeight() == getHeight()) && + (texture.getNumSamples() == getNumSamples())) { + return true; + } else { + return false; + } + } +} + +void Framebuffer::updateSize(const TexturePointer& texture) { + if (!isEmpty()) { + return; + } + + if (texture) { + _width = texture->getWidth(); + _height = texture->getHeight(); + _numSamples = texture->getNumSamples(); + } else { + _width = _height = _numSamples = 0; + } +} + +void Framebuffer::resize(uint16 width, uint16 height, uint16 numSamples) { + if (width && height && numSamples && !isEmpty() && !isSwapchain()) { + if ((width != _width) || (height != _height) || (numSamples != _numSamples)) { + for (uint32 i = 0; i < _renderBuffers.size(); ++i) { + if (_renderBuffers[i]) { + _renderBuffers[i]._texture->resize2D(width, height, numSamples); + _numSamples = _renderBuffers[i]._texture->getNumSamples(); + } + } + + if (_depthStencilBuffer) { + _depthStencilBuffer._texture->resize2D(width, height, numSamples); + _numSamples = _depthStencilBuffer._texture->getNumSamples(); + } + + _width = width; + _height = height; + // _numSamples = numSamples; + } + } +} + +uint16 Framebuffer::getWidth() const { + if (isSwapchain()) { + return getSwapchain()->getWidth(); + } else { + return _width; + } +} + +uint16 Framebuffer::getHeight() const { + if (isSwapchain()) { + return getSwapchain()->getHeight(); + } else { + return _height; + } +} + +uint16 Framebuffer::getNumSamples() const { + if (isSwapchain()) { + return getSwapchain()->getNumSamples(); + } else { + return _numSamples; + } +} + +// Render buffers +int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uint32 subresource) { + if (isSwapchain()) { + return -1; + } + + // Check for the slot + if (slot >= getMaxNumRenderBuffers()) { + return -1; + } + + // Check for the compatibility of size + if (texture) { + if (!validateTargetCompatibility(*texture, subresource)) { + return -1; + } + } + + updateSize(texture); + + // assign the new one + _renderBuffers[slot] = TextureView(texture, subresource); + + // update the mask + int mask = (1<getDepthStencilBufferFormat(); + return _depthStencilBuffer._element; + } else { + return _depthStencilBuffer._element; + } } \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index 83ff8fbb23..807047a56e 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -1,167 +1,167 @@ -// -// Framebuffer.h -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 4/12/2015. -// 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_gpu_Framebuffer_h -#define hifi_gpu_Framebuffer_h - -#include "Texture.h" -#include - -namespace gpu { - -typedef Element Format; - -class Swapchain { -public: - // Properties - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint16 getNumSamples() const { return _numSamples; } - - bool hasDepthStencil() const { return _hasDepthStencil; } - bool isFullscreen() const { return _isFullscreen; } - - uint32 getSwapInterval() const { return _swapInterval; } - - bool isStereo() const { return _isStereo; } - - uint32 getFrameCount() const { return _frameCount; } - - // Pure interface - void setSwapInterval(uint32 interval); - - void resize(uint16 width, uint16 height); - void setFullscreen(bool fullscreen); - - Swapchain() {} - Swapchain(const Swapchain& swapchain) {} - virtual ~Swapchain() {} - -protected: - mutable uint32 _frameCount = 0; - - Format _colorFormat; - uint16 _width = 1; - uint16 _height = 1; - uint16 _numSamples = 1; - uint16 _swapInterval = 0; - - bool _hasDepthStencil = false; - bool _isFullscreen = false; - bool _isStereo = false; - - // Non exposed - - friend class Framebuffer; -}; -typedef std::shared_ptr SwapchainPointer; - - -class Framebuffer { -public: - enum BufferMask { - BUFFER_COLOR0 = 1, - BUFFER_COLOR1 = 2, - BUFFER_COLOR2 = 4, - BUFFER_COLOR3 = 8, - BUFFER_COLOR4 = 16, - BUFFER_COLOR5 = 32, - BUFFER_COLOR6 = 64, - BUFFER_COLOR7 = 128, - BUFFER_COLORS = 0x000000FF, - - BUFFER_DEPTH = 0x40000000, - BUFFER_STENCIL = 0x80000000, - BUFFER_DEPTHSTENCIL = 0xC0000000, - }; - typedef uint32 Masks; - - ~Framebuffer(); - - static Framebuffer* create(const SwapchainPointer& swapchain); - static Framebuffer* create(); - static Framebuffer* create(const Format& colorBufferFormat, uint16 width, uint16 height); - static Framebuffer* create(const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height); - static Framebuffer* createShadowmap(uint16 width); - - bool isSwapchain() const; - SwapchainPointer getSwapchain() const { return _swapchain; } - - uint32 getFrameCount() const; - - // Render buffers - void removeRenderBuffers(); - uint32 getNumRenderBuffers() const; - const TextureViews& getRenderBuffers() const { return _renderBuffers; } - - int32 setRenderBuffer(uint32 slot, const TexturePointer& texture, uint32 subresource = 0); - TexturePointer getRenderBuffer(uint32 slot) const; - uint32 getRenderBufferSubresource(uint32 slot) const; - - bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0); - TexturePointer getDepthStencilBuffer() const; - uint32 getDepthStencilBufferSubresource() const; - Format getDepthStencilBufferFormat() const; - - - // Properties - Masks getBufferMask() const { return _bufferMask; } - bool isEmpty() const { return (_bufferMask == 0); } - bool hasColor() const { return (getBufferMask() & BUFFER_COLORS); } - bool hasDepthStencil() const { return (getBufferMask() & BUFFER_DEPTHSTENCIL); } - bool hasDepth() const { return (getBufferMask() & BUFFER_DEPTH); } - bool hasStencil() const { return (getBufferMask() & BUFFER_STENCIL); } - - bool validateTargetCompatibility(const Texture& texture, uint32 subresource = 0) const; - - Vec2u getSize() const { return Vec2u(getWidth(), getHeight()); } - uint16 getWidth() const; - uint16 getHeight() const; - uint16 getNumSamples() const; - - float getAspectRatio() const { return getWidth() / (float) getHeight() ; } - - // If not a swapchain canvas, resize can resize all the render buffers and depth stencil attached in one call - void resize( uint16 width, uint16 height, uint16 samples = 1 ); - - static const uint32 MAX_NUM_RENDER_BUFFERS = 8; - static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } - -protected: - SwapchainPointer _swapchain; - - TextureViews _renderBuffers; - TextureView _depthStencilBuffer; - - Masks _bufferMask = 0; - - uint32 _frameCount = 0; - - uint16 _width = 0; - uint16 _height = 0; - uint16 _numSamples = 0; - - void updateSize(const TexturePointer& texture); - - // Non exposed - Framebuffer(const Framebuffer& framebuffer) = delete; - Framebuffer() {} - - // This shouldn't be used by anything else than the Backend class with the proper casting. - mutable GPUObject* _gpuObject = NULL; - void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } - GPUObject* getGPUObject() const { return _gpuObject; } - friend class Backend; -}; -typedef std::shared_ptr FramebufferPointer; - -} - -#endif +// +// Framebuffer.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 4/12/2015. +// 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_gpu_Framebuffer_h +#define hifi_gpu_Framebuffer_h + +#include "Texture.h" +#include + +namespace gpu { + +typedef Element Format; + +class Swapchain { +public: + // Properties + uint16 getWidth() const { return _width; } + uint16 getHeight() const { return _height; } + uint16 getNumSamples() const { return _numSamples; } + + bool hasDepthStencil() const { return _hasDepthStencil; } + bool isFullscreen() const { return _isFullscreen; } + + uint32 getSwapInterval() const { return _swapInterval; } + + bool isStereo() const { return _isStereo; } + + uint32 getFrameCount() const { return _frameCount; } + + // Pure interface + void setSwapInterval(uint32 interval); + + void resize(uint16 width, uint16 height); + void setFullscreen(bool fullscreen); + + Swapchain() {} + Swapchain(const Swapchain& swapchain) {} + virtual ~Swapchain() {} + +protected: + mutable uint32 _frameCount = 0; + + Format _colorFormat; + uint16 _width = 1; + uint16 _height = 1; + uint16 _numSamples = 1; + uint16 _swapInterval = 0; + + bool _hasDepthStencil = false; + bool _isFullscreen = false; + bool _isStereo = false; + + // Non exposed + + friend class Framebuffer; +}; +typedef std::shared_ptr SwapchainPointer; + + +class Framebuffer { +public: + enum BufferMask { + BUFFER_COLOR0 = 1, + BUFFER_COLOR1 = 2, + BUFFER_COLOR2 = 4, + BUFFER_COLOR3 = 8, + BUFFER_COLOR4 = 16, + BUFFER_COLOR5 = 32, + BUFFER_COLOR6 = 64, + BUFFER_COLOR7 = 128, + BUFFER_COLORS = 0x000000FF, + + BUFFER_DEPTH = 0x40000000, + BUFFER_STENCIL = 0x80000000, + BUFFER_DEPTHSTENCIL = 0xC0000000, + }; + typedef uint32 Masks; + + ~Framebuffer(); + + static Framebuffer* create(const SwapchainPointer& swapchain); + static Framebuffer* create(); + static Framebuffer* create(const Format& colorBufferFormat, uint16 width, uint16 height); + static Framebuffer* create(const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height); + static Framebuffer* createShadowmap(uint16 width); + + bool isSwapchain() const; + SwapchainPointer getSwapchain() const { return _swapchain; } + + uint32 getFrameCount() const; + + // Render buffers + void removeRenderBuffers(); + uint32 getNumRenderBuffers() const; + const TextureViews& getRenderBuffers() const { return _renderBuffers; } + + int32 setRenderBuffer(uint32 slot, const TexturePointer& texture, uint32 subresource = 0); + TexturePointer getRenderBuffer(uint32 slot) const; + uint32 getRenderBufferSubresource(uint32 slot) const; + + bool setDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource = 0); + TexturePointer getDepthStencilBuffer() const; + uint32 getDepthStencilBufferSubresource() const; + Format getDepthStencilBufferFormat() const; + + + // Properties + Masks getBufferMask() const { return _bufferMask; } + bool isEmpty() const { return (_bufferMask == 0); } + bool hasColor() const { return (getBufferMask() & BUFFER_COLORS); } + bool hasDepthStencil() const { return (getBufferMask() & BUFFER_DEPTHSTENCIL); } + bool hasDepth() const { return (getBufferMask() & BUFFER_DEPTH); } + bool hasStencil() const { return (getBufferMask() & BUFFER_STENCIL); } + + bool validateTargetCompatibility(const Texture& texture, uint32 subresource = 0) const; + + Vec2u getSize() const { return Vec2u(getWidth(), getHeight()); } + uint16 getWidth() const; + uint16 getHeight() const; + uint16 getNumSamples() const; + + float getAspectRatio() const { return getWidth() / (float) getHeight() ; } + + // If not a swapchain canvas, resize can resize all the render buffers and depth stencil attached in one call + void resize( uint16 width, uint16 height, uint16 samples = 1 ); + + static const uint32 MAX_NUM_RENDER_BUFFERS = 8; + static uint32 getMaxNumRenderBuffers() { return MAX_NUM_RENDER_BUFFERS; } + +protected: + SwapchainPointer _swapchain; + + TextureViews _renderBuffers; + TextureView _depthStencilBuffer; + + Masks _bufferMask = 0; + + uint32 _frameCount = 0; + + uint16 _width = 0; + uint16 _height = 0; + uint16 _numSamples = 0; + + void updateSize(const TexturePointer& texture); + + // Non exposed + Framebuffer(const Framebuffer& framebuffer) = delete; + Framebuffer() {} + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + friend class Backend; +}; +typedef std::shared_ptr FramebufferPointer; + +} + +#endif diff --git a/libraries/gpu/src/gpu/Pipeline.cpp b/libraries/gpu/src/gpu/Pipeline.cpp index 237932b07d..501cb3fea4 100755 --- a/libraries/gpu/src/gpu/Pipeline.cpp +++ b/libraries/gpu/src/gpu/Pipeline.cpp @@ -1,29 +1,29 @@ -// -// Pipeline.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 3/8/2015. -// 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 "Pipeline.h" -#include -#include - -using namespace gpu; - -Pipeline::Pipeline(): - _program(), - _state() -{ -} - -Pipeline::~Pipeline() -{ -} +// +// Pipeline.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// 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 "Pipeline.h" +#include +#include + +using namespace gpu; + +Pipeline::Pipeline(): + _program(), + _state() +{ +} + +Pipeline::~Pipeline() +{ +} Pipeline* Pipeline::create(const ShaderPointer& program, const StatePointer& state) { Pipeline* pipeline = new Pipeline(); diff --git a/libraries/gpu/src/gpu/State.cpp b/libraries/gpu/src/gpu/State.cpp index da3ab20c7b..cb5ced2c15 100755 --- a/libraries/gpu/src/gpu/State.cpp +++ b/libraries/gpu/src/gpu/State.cpp @@ -1,88 +1,88 @@ -// -// State.cpp -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 3/8/2015. -// 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 "State.h" -#include - -using namespace gpu; - -State::State() { -} - -State::~State() { -} - -// WARNING: GLBackend::GLState::_resetStateCommands heavily relies on the fact that State::DEFAULT = State::Data() -// Please make sure to go check makeResetStateCommands() before modifying this value -const State::Data State::DEFAULT = State::Data(); - -State::Signature State::evalSignature(const Data& state) { - Signature signature(0); - - if (state.fillMode != State::DEFAULT.fillMode) { - signature.set(State::FILL_MODE); - } - if (state.cullMode != State::DEFAULT.cullMode) { - signature.set(State::CULL_MODE); - } - if (state.frontFaceClockwise != State::DEFAULT.frontFaceClockwise) { - signature.set(State::FRONT_FACE_CLOCKWISE); - } - if (state.depthClampEnable != State::DEFAULT.depthClampEnable) { - signature.set(State::DEPTH_CLAMP_ENABLE); - } - if (state.scissorEnable != State::DEFAULT.scissorEnable) { - signature.set(State::SCISSOR_ENABLE); - } - if (state.multisampleEnable != State::DEFAULT.multisampleEnable) { - signature.set(State::MULTISAMPLE_ENABLE); - } - if (state.antialisedLineEnable != State::DEFAULT.antialisedLineEnable) { - signature.set(State::ANTIALISED_LINE_ENABLE); - } - if (state.depthBias != State::DEFAULT.depthBias) { - signature.set(State::DEPTH_BIAS); - } - if (state.depthBiasSlopeScale != State::DEFAULT.depthBiasSlopeScale) { - signature.set(State::DEPTH_BIAS_SLOPE_SCALE); - } - if (state.depthTest != State::DEFAULT.depthTest) { - signature.set(State::DEPTH_TEST); - } - if (state.stencilActivation != State::DEFAULT.stencilActivation) { - signature.set(State::STENCIL_ACTIVATION); - } - if (state.stencilTestFront != State::DEFAULT.stencilTestFront) { - signature.set(State::STENCIL_TEST_FRONT); - } - if (state.stencilTestBack != State::DEFAULT.stencilTestBack) { - signature.set(State::STENCIL_TEST_BACK); - } - if (state.sampleMask != State::DEFAULT.sampleMask) { - signature.set(State::SAMPLE_MASK); - } - if (state.alphaToCoverageEnable != State::DEFAULT.alphaToCoverageEnable) { - signature.set(State::ALPHA_TO_COVERAGE_ENABLE); - } - if (state.blendFunction != State::DEFAULT.blendFunction) { - signature.set(State::BLEND_FUNCTION); - } - if (state.colorWriteMask != State::DEFAULT.colorWriteMask) { - signature.set(State::COLOR_WRITE_MASK); - } - - return signature; -} - -State::State(const Data& values) : - _values(values) { - _signature = evalSignature(_values); -} +// +// State.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 3/8/2015. +// 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 "State.h" +#include + +using namespace gpu; + +State::State() { +} + +State::~State() { +} + +// WARNING: GLBackend::GLState::_resetStateCommands heavily relies on the fact that State::DEFAULT = State::Data() +// Please make sure to go check makeResetStateCommands() before modifying this value +const State::Data State::DEFAULT = State::Data(); + +State::Signature State::evalSignature(const Data& state) { + Signature signature(0); + + if (state.fillMode != State::DEFAULT.fillMode) { + signature.set(State::FILL_MODE); + } + if (state.cullMode != State::DEFAULT.cullMode) { + signature.set(State::CULL_MODE); + } + if (state.frontFaceClockwise != State::DEFAULT.frontFaceClockwise) { + signature.set(State::FRONT_FACE_CLOCKWISE); + } + if (state.depthClampEnable != State::DEFAULT.depthClampEnable) { + signature.set(State::DEPTH_CLAMP_ENABLE); + } + if (state.scissorEnable != State::DEFAULT.scissorEnable) { + signature.set(State::SCISSOR_ENABLE); + } + if (state.multisampleEnable != State::DEFAULT.multisampleEnable) { + signature.set(State::MULTISAMPLE_ENABLE); + } + if (state.antialisedLineEnable != State::DEFAULT.antialisedLineEnable) { + signature.set(State::ANTIALISED_LINE_ENABLE); + } + if (state.depthBias != State::DEFAULT.depthBias) { + signature.set(State::DEPTH_BIAS); + } + if (state.depthBiasSlopeScale != State::DEFAULT.depthBiasSlopeScale) { + signature.set(State::DEPTH_BIAS_SLOPE_SCALE); + } + if (state.depthTest != State::DEFAULT.depthTest) { + signature.set(State::DEPTH_TEST); + } + if (state.stencilActivation != State::DEFAULT.stencilActivation) { + signature.set(State::STENCIL_ACTIVATION); + } + if (state.stencilTestFront != State::DEFAULT.stencilTestFront) { + signature.set(State::STENCIL_TEST_FRONT); + } + if (state.stencilTestBack != State::DEFAULT.stencilTestBack) { + signature.set(State::STENCIL_TEST_BACK); + } + if (state.sampleMask != State::DEFAULT.sampleMask) { + signature.set(State::SAMPLE_MASK); + } + if (state.alphaToCoverageEnable != State::DEFAULT.alphaToCoverageEnable) { + signature.set(State::ALPHA_TO_COVERAGE_ENABLE); + } + if (state.blendFunction != State::DEFAULT.blendFunction) { + signature.set(State::BLEND_FUNCTION); + } + if (state.colorWriteMask != State::DEFAULT.colorWriteMask) { + signature.set(State::COLOR_WRITE_MASK); + } + + return signature; +} + +State::State(const Data& values) : + _values(values) { + _signature = evalSignature(_values); +} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index e1855e0848..c3e2809c4b 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -1,448 +1,448 @@ -// -// Texture.h -// libraries/gpu/src/gpu -// -// Created by Sam Gateau on 1/16/2015. -// 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_gpu_Texture_h -#define hifi_gpu_Texture_h - -#include "Resource.h" - -#include //min max and more - -#include - -namespace gpu { - -// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated -// with the cube texture -class Texture; -class SphericalHarmonics { -public: - glm::vec3 L00 ; float spare0; - glm::vec3 L1m1 ; float spare1; - glm::vec3 L10 ; float spare2; - glm::vec3 L11 ; float spare3; - glm::vec3 L2m2 ; float spare4; - glm::vec3 L2m1 ; float spare5; - glm::vec3 L20 ; float spare6; - glm::vec3 L21 ; float spare7; - glm::vec3 L22 ; float spare8; - - static const int NUM_COEFFICIENTS = 9; - - enum Preset { - OLD_TOWN_SQUARE = 0, - GRACE_CATHEDRAL, - EUCALYPTUS_GROVE, - ST_PETERS_BASILICA, - UFFIZI_GALLERY, - GALILEOS_TOMB, - VINE_STREET_KITCHEN, - BREEZEWAY, - CAMPUS_SUNSET, - FUNSTON_BEACH_SUNSET, - - NUM_PRESET, - }; - - void assignPreset(int p); - - void evalFromTexture(const Texture& texture); -}; -typedef std::shared_ptr< SphericalHarmonics > SHPointer; - -class Sampler { -public: - - enum Filter { - FILTER_MIN_MAG_POINT, // top mip only - FILTER_MIN_POINT_MAG_LINEAR, // top mip only - FILTER_MIN_LINEAR_MAG_POINT, // top mip only - FILTER_MIN_MAG_LINEAR, // top mip only - - FILTER_MIN_MAG_MIP_POINT, - FILTER_MIN_MAG_POINT_MIP_LINEAR, - FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, - FILTER_MIN_POINT_MAG_MIP_LINEAR, - FILTER_MIN_LINEAR_MAG_MIP_POINT, - FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, - FILTER_MIN_MAG_LINEAR_MIP_POINT, - FILTER_MIN_MAG_MIP_LINEAR, - FILTER_ANISOTROPIC, - - NUM_FILTERS, - }; - - enum WrapMode { - WRAP_REPEAT = 0, - WRAP_MIRROR, - WRAP_CLAMP, - WRAP_BORDER, - WRAP_MIRROR_ONCE, - - NUM_WRAP_MODES - }; - - static const uint8 MAX_MIP_LEVEL = 0xFF; - - class Desc { - public: - glm::vec4 _borderColor{ 1.0f }; - uint32 _maxAnisotropy = 16; - - uint8 _filter = FILTER_MIN_MAG_POINT; - uint8 _comparisonFunc = ALWAYS; - - uint8 _wrapModeU = WRAP_REPEAT; - uint8 _wrapModeV = WRAP_REPEAT; - uint8 _wrapModeW = WRAP_REPEAT; - - uint8 _mipOffset = 0; - uint8 _minMip = 0; - uint8 _maxMip = MAX_MIP_LEVEL; - - Desc() {} - Desc(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _filter(filter), _wrapModeU(wrap), _wrapModeV(wrap), _wrapModeW(wrap) {} - }; - - Sampler() {} - Sampler(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _desc(filter, wrap) {} - Sampler(const Desc& desc) : _desc(desc) {} - ~Sampler() {} - - const glm::vec4& getBorderColor() const { return _desc._borderColor; } - - uint32 getMaxAnisotropy() const { return _desc._maxAnisotropy; } - - WrapMode getWrapModeU() const { return WrapMode(_desc._wrapModeU); } - WrapMode getWrapModeV() const { return WrapMode(_desc._wrapModeV); } - WrapMode getWrapModeW() const { return WrapMode(_desc._wrapModeW); } - - Filter getFilter() const { return Filter(_desc._filter); } - ComparisonFunction getComparisonFunction() const { return ComparisonFunction(_desc._comparisonFunc); } - bool doComparison() const { return getComparisonFunction() != ALWAYS; } - - uint8 getMipOffset() const { return _desc._mipOffset; } - uint8 getMinMip() const { return _desc._minMip; } - uint8 getMaxMip() const { return _desc._maxMip; } - -protected: - Desc _desc; -}; - -class Texture : public Resource { -public: - - class Pixels { - public: - Pixels() {} - Pixels(const Pixels& pixels) = default; - Pixels(const Element& format, Size size, const Byte* bytes); - ~Pixels(); - - Sysmem _sysmem; - Element _format; - bool _isGPULoaded; - }; - typedef std::shared_ptr< Pixels > PixelsPointer; - - enum Type { - TEX_1D = 0, - TEX_2D, - TEX_3D, - TEX_CUBE, - - NUM_TYPES, - }; - - // Definition of the cube face name and layout - enum CubeFace { - CUBE_FACE_RIGHT_POS_X = 0, - CUBE_FACE_LEFT_NEG_X, - CUBE_FACE_TOP_POS_Y, - CUBE_FACE_BOTTOM_NEG_Y, - CUBE_FACE_BACK_POS_Z, - CUBE_FACE_FRONT_NEG_Z, - - NUM_CUBE_FACES, // Not a valid vace index - }; - - class Storage { - public: - Storage() {} - virtual ~Storage() {} - virtual void reset(); - virtual PixelsPointer editMipFace(uint16 level, uint8 face = 0); - virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const; - virtual bool allocateMip(uint16 level); - virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes); - virtual bool assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); - virtual bool isMipAvailable(uint16 level, uint8 face = 0) const; - - Texture::Type getType() const { return _type; } - - Stamp getStamp() const { return _stamp; } - Stamp bumpStamp() { return ++_stamp; } - protected: - Stamp _stamp = 0; - Texture* _texture = nullptr; - Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect - std::vector> _mips; // an array of mips, each mip is an array of faces - - virtual void assignTexture(Texture* tex); // Texture storage is pointing to ONE corrresponding Texture. - const Texture* getTexture() const { return _texture; } - - friend class Texture; - - // THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels - // have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then - virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const; - }; - - - static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); - static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); - static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - - static Texture* createFromStorage(Storage* storage); - - Texture(); - Texture(const Texture& buf); // deep copy of the sysmem texture - Texture& operator=(const Texture& buf); // deep copy of the sysmem texture - ~Texture(); - - Stamp getStamp() const { return _stamp; } - Stamp getDataStamp() const { return _storage->getStamp(); } - - // The size in bytes of data stored in the texture - Size getSize() const { return _size; } - - // Resize, unless auto mips mode would destroy all the sub mips - Size resize1D(uint16 width, uint16 numSamples); - Size resize2D(uint16 width, uint16 height, uint16 numSamples); - Size resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples); - Size resizeCube(uint16 width, uint16 numSamples); - - // Reformat, unless auto mips mode would destroy all the sub mips - Size reformat(const Element& texelFormat); - - // Size and format - Type getType() const { return _type; } - - bool isColorRenderTarget() const; - bool isDepthStencilRenderTarget() const; - - const Element& getTexelFormat() const { return _texelFormat; } - bool hasBorder() const { return false; } - - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint16 getDepth() const { return _depth; } - - uint32 getRowPitch() const { return getWidth() * getTexelFormat().getSize(); } - - // The number of faces is mostly used for cube map, and maybe for stereo ? otherwise it's 1 - // For cube maps, this means the pixels of the different faces are supposed to be packed back to back in a mip - // as if the height was NUM_FACES time bigger. - static uint8 NUM_FACES_PER_TYPE[NUM_TYPES]; - uint8 getNumFaces() const { return NUM_FACES_PER_TYPE[getType()]; } - - uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } - - uint16 getNumSlices() const { return _numSlices; } - uint16 getNumSamples() const { return _numSamples; } - - - // NumSamples can only have certain values based on the hw - static uint16 evalNumSamplesUsed(uint16 numSamplesTried); - - // Mips size evaluation - - // The number mips that a dimension could haves - // = 1 + log2(size) - static uint16 evalDimNumMips(uint16 size); - - // The number mips that the texture could have if all existed - // = 1 + log2(max(width, height, depth)) - uint16 evalNumMips() const; - - // Eval the size that the mips level SHOULD have - // not the one stored in the Texture - uint16 evalMipWidth(uint16 level) const { return std::max(_width >> level, 1); } - uint16 evalMipHeight(uint16 level) const { return std::max(_height >> level, 1); } - uint16 evalMipDepth(uint16 level) const { return std::max(_depth >> level, 1); } - - // Size for each face of a mip at a particular level - uint32 evalMipFaceNumTexels(uint16 level) const { return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level); } - uint32 evalMipFaceSize(uint16 level) const { return evalMipFaceNumTexels(level) * getTexelFormat().getSize(); } - - // Total size for the mip - uint32 evalMipNumTexels(uint16 level) const { return evalMipFaceNumTexels(level) * getNumFaces(); } - uint32 evalMipSize(uint16 level) const { return evalMipNumTexels(level) * getTexelFormat().getSize(); } - - uint32 evalStoredMipFaceSize(uint16 level, const Element& format) const { return evalMipFaceNumTexels(level) * format.getSize(); } - uint32 evalStoredMipSize(uint16 level, const Element& format) const { return evalMipNumTexels(level) * format.getSize(); } - - uint32 evalTotalSize() const { - uint32 size = 0; - uint16 minMipLevel = 0; - uint16 maxMipLevel = maxMip(); - for (uint16 l = minMipLevel; l <= maxMipLevel; l++) { - size += evalMipSize(l); - } - return size * getNumSlices(); - } - - // max mip is in the range [ 1 if no sub mips, log2(max(width, height, depth))] - // if autoGenerateMip is on => will provide the maxMIp level specified - // else provide the deepest mip level provided through assignMip - uint16 maxMip() const; - - // Generate the mips automatically - // But the sysmem version is not available - // Only works for the standard formats - // Specify the maximum Mip level available - // 0 is the default one - // 1 is the first level - // ... - // nbMips - 1 is the last mip level - // - // If -1 then all the mips are generated - // - // Return the totalnumber of mips that will be available - uint16 autoGenerateMips(uint16 maxMip); - bool isAutogenerateMips() const { return _autoGenerateMips; } - - // Managing Storage and mips - - // Manually allocate the mips down until the specified maxMip - // this is just allocating the sysmem version of it - // in case autoGen is on, this doesn't allocate - // Explicitely assign mip data for a certain level - // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed - bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes); - bool assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); - - // Access the the sub mips - bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } - const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } - - // access sizes for the stored mips - uint16 getStoredMipWidth(uint16 level) const; - uint16 getStoredMipHeight(uint16 level) const; - uint16 getStoredMipDepth(uint16 level) const; - uint32 getStoredMipNumTexels(uint16 level) const; - uint32 getStoredMipSize(uint16 level) const; - - bool isDefined() const { return _defined; } - - // For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture - bool generateIrradiance(); - const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; } - bool isIrradianceValid() const { return _isIrradianceValid; } - - // Own sampler - void setSampler(const Sampler& sampler); - const Sampler& getSampler() const { return _sampler; } - Stamp getSamplerStamp() const { return _samplerStamp; } - - // Only callable by the Backend - void notifyMipFaceGPULoaded(uint16 level, uint8 face) const { return _storage->notifyMipFaceGPULoaded(level, face); } - -protected: - std::unique_ptr< Storage > _storage; - - Stamp _stamp = 0; - - Sampler _sampler; - Stamp _samplerStamp; - - uint32 _size = 0; - Element _texelFormat; - - uint16 _width = 1; - uint16 _height = 1; - uint16 _depth = 1; - - uint16 _numSamples = 1; - uint16 _numSlices = 1; - - uint16 _maxMip = 0; - - Type _type = TEX_1D; - - SHPointer _irradiance; - bool _autoGenerateMips = false; - bool _isIrradianceValid = false; - bool _defined = false; - - static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); - - Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); - - // This shouldn't be used by anything else than the Backend class with the proper casting. - mutable GPUObject* _gpuObject = NULL; - void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } - GPUObject* getGPUObject() const { return _gpuObject; } - - friend class Backend; -}; - -typedef std::shared_ptr TexturePointer; -typedef std::vector< TexturePointer > Textures; - - - // TODO: For now TextureView works with Buffer as a place holder for the Texture. - // The overall logic should be about the same except that the Texture will be a real GL Texture under the hood -class TextureView { -public: - typedef Resource::Size Size; - - TexturePointer _texture = TexturePointer(NULL); - uint16 _subresource = 0; - Element _element = Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); - - TextureView() {}; - - TextureView(const Element& element) : - _element(element) - {}; - - // create the TextureView and own the Texture - TextureView(Texture* newTexture, const Element& element) : - _texture(newTexture), - _subresource(0), - _element(element) - {}; - TextureView(const TexturePointer& texture, uint16 subresource, const Element& element) : - _texture(texture), - _subresource(subresource), - _element(element) - {}; - - TextureView(const TexturePointer& texture, uint16 subresource) : - _texture(texture), - _subresource(subresource) - {}; - - ~TextureView() {} - TextureView(const TextureView& view) = default; - TextureView& operator=(const TextureView& view) = default; - - explicit operator bool() const { return bool(_texture); } - bool operator !() const { return (!_texture); } - - bool isValid() const { return bool(_texture); } -}; -typedef std::vector TextureViews; - +// +// Texture.h +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 1/16/2015. +// 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_gpu_Texture_h +#define hifi_gpu_Texture_h + +#include "Resource.h" + +#include //min max and more + +#include + +namespace gpu { + +// THe spherical harmonics is a nice tool for cubemap, so if required, the irradiance SH can be automatically generated +// with the cube texture +class Texture; +class SphericalHarmonics { +public: + glm::vec3 L00 ; float spare0; + glm::vec3 L1m1 ; float spare1; + glm::vec3 L10 ; float spare2; + glm::vec3 L11 ; float spare3; + glm::vec3 L2m2 ; float spare4; + glm::vec3 L2m1 ; float spare5; + glm::vec3 L20 ; float spare6; + glm::vec3 L21 ; float spare7; + glm::vec3 L22 ; float spare8; + + static const int NUM_COEFFICIENTS = 9; + + enum Preset { + OLD_TOWN_SQUARE = 0, + GRACE_CATHEDRAL, + EUCALYPTUS_GROVE, + ST_PETERS_BASILICA, + UFFIZI_GALLERY, + GALILEOS_TOMB, + VINE_STREET_KITCHEN, + BREEZEWAY, + CAMPUS_SUNSET, + FUNSTON_BEACH_SUNSET, + + NUM_PRESET, + }; + + void assignPreset(int p); + + void evalFromTexture(const Texture& texture); +}; +typedef std::shared_ptr< SphericalHarmonics > SHPointer; + +class Sampler { +public: + + enum Filter { + FILTER_MIN_MAG_POINT, // top mip only + FILTER_MIN_POINT_MAG_LINEAR, // top mip only + FILTER_MIN_LINEAR_MAG_POINT, // top mip only + FILTER_MIN_MAG_LINEAR, // top mip only + + FILTER_MIN_MAG_MIP_POINT, + FILTER_MIN_MAG_POINT_MIP_LINEAR, + FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, + FILTER_MIN_POINT_MAG_MIP_LINEAR, + FILTER_MIN_LINEAR_MAG_MIP_POINT, + FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, + FILTER_MIN_MAG_LINEAR_MIP_POINT, + FILTER_MIN_MAG_MIP_LINEAR, + FILTER_ANISOTROPIC, + + NUM_FILTERS, + }; + + enum WrapMode { + WRAP_REPEAT = 0, + WRAP_MIRROR, + WRAP_CLAMP, + WRAP_BORDER, + WRAP_MIRROR_ONCE, + + NUM_WRAP_MODES + }; + + static const uint8 MAX_MIP_LEVEL = 0xFF; + + class Desc { + public: + glm::vec4 _borderColor{ 1.0f }; + uint32 _maxAnisotropy = 16; + + uint8 _filter = FILTER_MIN_MAG_POINT; + uint8 _comparisonFunc = ALWAYS; + + uint8 _wrapModeU = WRAP_REPEAT; + uint8 _wrapModeV = WRAP_REPEAT; + uint8 _wrapModeW = WRAP_REPEAT; + + uint8 _mipOffset = 0; + uint8 _minMip = 0; + uint8 _maxMip = MAX_MIP_LEVEL; + + Desc() {} + Desc(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _filter(filter), _wrapModeU(wrap), _wrapModeV(wrap), _wrapModeW(wrap) {} + }; + + Sampler() {} + Sampler(const Filter filter, const WrapMode wrap = WRAP_REPEAT) : _desc(filter, wrap) {} + Sampler(const Desc& desc) : _desc(desc) {} + ~Sampler() {} + + const glm::vec4& getBorderColor() const { return _desc._borderColor; } + + uint32 getMaxAnisotropy() const { return _desc._maxAnisotropy; } + + WrapMode getWrapModeU() const { return WrapMode(_desc._wrapModeU); } + WrapMode getWrapModeV() const { return WrapMode(_desc._wrapModeV); } + WrapMode getWrapModeW() const { return WrapMode(_desc._wrapModeW); } + + Filter getFilter() const { return Filter(_desc._filter); } + ComparisonFunction getComparisonFunction() const { return ComparisonFunction(_desc._comparisonFunc); } + bool doComparison() const { return getComparisonFunction() != ALWAYS; } + + uint8 getMipOffset() const { return _desc._mipOffset; } + uint8 getMinMip() const { return _desc._minMip; } + uint8 getMaxMip() const { return _desc._maxMip; } + +protected: + Desc _desc; +}; + +class Texture : public Resource { +public: + + class Pixels { + public: + Pixels() {} + Pixels(const Pixels& pixels) = default; + Pixels(const Element& format, Size size, const Byte* bytes); + ~Pixels(); + + Sysmem _sysmem; + Element _format; + bool _isGPULoaded; + }; + typedef std::shared_ptr< Pixels > PixelsPointer; + + enum Type { + TEX_1D = 0, + TEX_2D, + TEX_3D, + TEX_CUBE, + + NUM_TYPES, + }; + + // Definition of the cube face name and layout + enum CubeFace { + CUBE_FACE_RIGHT_POS_X = 0, + CUBE_FACE_LEFT_NEG_X, + CUBE_FACE_TOP_POS_Y, + CUBE_FACE_BOTTOM_NEG_Y, + CUBE_FACE_BACK_POS_Z, + CUBE_FACE_FRONT_NEG_Z, + + NUM_CUBE_FACES, // Not a valid vace index + }; + + class Storage { + public: + Storage() {} + virtual ~Storage() {} + virtual void reset(); + virtual PixelsPointer editMipFace(uint16 level, uint8 face = 0); + virtual const PixelsPointer getMipFace(uint16 level, uint8 face = 0) const; + virtual bool allocateMip(uint16 level); + virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes); + virtual bool assignMipFaceData(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); + virtual bool isMipAvailable(uint16 level, uint8 face = 0) const; + + Texture::Type getType() const { return _type; } + + Stamp getStamp() const { return _stamp; } + Stamp bumpStamp() { return ++_stamp; } + protected: + Stamp _stamp = 0; + Texture* _texture = nullptr; + Texture::Type _type = Texture::TEX_2D; // The type of texture is needed to know the number of faces to expect + std::vector> _mips; // an array of mips, each mip is an array of faces + + virtual void assignTexture(Texture* tex); // Texture storage is pointing to ONE corrresponding Texture. + const Texture* getTexture() const { return _texture; } + + friend class Texture; + + // THis should be only called by the Texture from the Backend to notify the storage that the specified mip face pixels + // have been uploaded to the GPU memory. IT is possible for the storage to free the system memory then + virtual void notifyMipFaceGPULoaded(uint16 level, uint8 face) const; + }; + + + static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); + static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); + static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); + static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); + + static Texture* createFromStorage(Storage* storage); + + Texture(); + Texture(const Texture& buf); // deep copy of the sysmem texture + Texture& operator=(const Texture& buf); // deep copy of the sysmem texture + ~Texture(); + + Stamp getStamp() const { return _stamp; } + Stamp getDataStamp() const { return _storage->getStamp(); } + + // The size in bytes of data stored in the texture + Size getSize() const { return _size; } + + // Resize, unless auto mips mode would destroy all the sub mips + Size resize1D(uint16 width, uint16 numSamples); + Size resize2D(uint16 width, uint16 height, uint16 numSamples); + Size resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples); + Size resizeCube(uint16 width, uint16 numSamples); + + // Reformat, unless auto mips mode would destroy all the sub mips + Size reformat(const Element& texelFormat); + + // Size and format + Type getType() const { return _type; } + + bool isColorRenderTarget() const; + bool isDepthStencilRenderTarget() const; + + const Element& getTexelFormat() const { return _texelFormat; } + bool hasBorder() const { return false; } + + uint16 getWidth() const { return _width; } + uint16 getHeight() const { return _height; } + uint16 getDepth() const { return _depth; } + + uint32 getRowPitch() const { return getWidth() * getTexelFormat().getSize(); } + + // The number of faces is mostly used for cube map, and maybe for stereo ? otherwise it's 1 + // For cube maps, this means the pixels of the different faces are supposed to be packed back to back in a mip + // as if the height was NUM_FACES time bigger. + static uint8 NUM_FACES_PER_TYPE[NUM_TYPES]; + uint8 getNumFaces() const { return NUM_FACES_PER_TYPE[getType()]; } + + uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } + + uint16 getNumSlices() const { return _numSlices; } + uint16 getNumSamples() const { return _numSamples; } + + + // NumSamples can only have certain values based on the hw + static uint16 evalNumSamplesUsed(uint16 numSamplesTried); + + // Mips size evaluation + + // The number mips that a dimension could haves + // = 1 + log2(size) + static uint16 evalDimNumMips(uint16 size); + + // The number mips that the texture could have if all existed + // = 1 + log2(max(width, height, depth)) + uint16 evalNumMips() const; + + // Eval the size that the mips level SHOULD have + // not the one stored in the Texture + uint16 evalMipWidth(uint16 level) const { return std::max(_width >> level, 1); } + uint16 evalMipHeight(uint16 level) const { return std::max(_height >> level, 1); } + uint16 evalMipDepth(uint16 level) const { return std::max(_depth >> level, 1); } + + // Size for each face of a mip at a particular level + uint32 evalMipFaceNumTexels(uint16 level) const { return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level); } + uint32 evalMipFaceSize(uint16 level) const { return evalMipFaceNumTexels(level) * getTexelFormat().getSize(); } + + // Total size for the mip + uint32 evalMipNumTexels(uint16 level) const { return evalMipFaceNumTexels(level) * getNumFaces(); } + uint32 evalMipSize(uint16 level) const { return evalMipNumTexels(level) * getTexelFormat().getSize(); } + + uint32 evalStoredMipFaceSize(uint16 level, const Element& format) const { return evalMipFaceNumTexels(level) * format.getSize(); } + uint32 evalStoredMipSize(uint16 level, const Element& format) const { return evalMipNumTexels(level) * format.getSize(); } + + uint32 evalTotalSize() const { + uint32 size = 0; + uint16 minMipLevel = 0; + uint16 maxMipLevel = maxMip(); + for (uint16 l = minMipLevel; l <= maxMipLevel; l++) { + size += evalMipSize(l); + } + return size * getNumSlices(); + } + + // max mip is in the range [ 1 if no sub mips, log2(max(width, height, depth))] + // if autoGenerateMip is on => will provide the maxMIp level specified + // else provide the deepest mip level provided through assignMip + uint16 maxMip() const; + + // Generate the mips automatically + // But the sysmem version is not available + // Only works for the standard formats + // Specify the maximum Mip level available + // 0 is the default one + // 1 is the first level + // ... + // nbMips - 1 is the last mip level + // + // If -1 then all the mips are generated + // + // Return the totalnumber of mips that will be available + uint16 autoGenerateMips(uint16 maxMip); + bool isAutogenerateMips() const { return _autoGenerateMips; } + + // Managing Storage and mips + + // Manually allocate the mips down until the specified maxMip + // this is just allocating the sysmem version of it + // in case autoGen is on, this doesn't allocate + // Explicitely assign mip data for a certain level + // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed + bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes); + bool assignStoredMipFace(uint16 level, const Element& format, Size size, const Byte* bytes, uint8 face); + + // Access the the sub mips + bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } + const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } + + // access sizes for the stored mips + uint16 getStoredMipWidth(uint16 level) const; + uint16 getStoredMipHeight(uint16 level) const; + uint16 getStoredMipDepth(uint16 level) const; + uint32 getStoredMipNumTexels(uint16 level) const; + uint32 getStoredMipSize(uint16 level) const; + + bool isDefined() const { return _defined; } + + // For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture + bool generateIrradiance(); + const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; } + bool isIrradianceValid() const { return _isIrradianceValid; } + + // Own sampler + void setSampler(const Sampler& sampler); + const Sampler& getSampler() const { return _sampler; } + Stamp getSamplerStamp() const { return _samplerStamp; } + + // Only callable by the Backend + void notifyMipFaceGPULoaded(uint16 level, uint8 face) const { return _storage->notifyMipFaceGPULoaded(level, face); } + +protected: + std::unique_ptr< Storage > _storage; + + Stamp _stamp = 0; + + Sampler _sampler; + Stamp _samplerStamp; + + uint32 _size = 0; + Element _texelFormat; + + uint16 _width = 1; + uint16 _height = 1; + uint16 _depth = 1; + + uint16 _numSamples = 1; + uint16 _numSlices = 1; + + uint16 _maxMip = 0; + + Type _type = TEX_1D; + + SHPointer _irradiance; + bool _autoGenerateMips = false; + bool _isIrradianceValid = false; + bool _defined = false; + + static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); + + Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + + friend class Backend; +}; + +typedef std::shared_ptr TexturePointer; +typedef std::vector< TexturePointer > Textures; + + + // TODO: For now TextureView works with Buffer as a place holder for the Texture. + // The overall logic should be about the same except that the Texture will be a real GL Texture under the hood +class TextureView { +public: + typedef Resource::Size Size; + + TexturePointer _texture = TexturePointer(NULL); + uint16 _subresource = 0; + Element _element = Element(gpu::VEC4, gpu::UINT8, gpu::RGBA); + + TextureView() {}; + + TextureView(const Element& element) : + _element(element) + {}; + + // create the TextureView and own the Texture + TextureView(Texture* newTexture, const Element& element) : + _texture(newTexture), + _subresource(0), + _element(element) + {}; + TextureView(const TexturePointer& texture, uint16 subresource, const Element& element) : + _texture(texture), + _subresource(subresource), + _element(element) + {}; + + TextureView(const TexturePointer& texture, uint16 subresource) : + _texture(texture), + _subresource(subresource) + {}; + + ~TextureView() {} + TextureView(const TextureView& view) = default; + TextureView& operator=(const TextureView& view) = default; + + explicit operator bool() const { return bool(_texture); } + bool operator !() const { return (!_texture); } + + bool isValid() const { return bool(_texture); } +}; +typedef std::vector TextureViews; + // TextureSource is the bridge between a URL or a a way to produce an image and the final gpu::Texture that will be used to render it. // It provides the mechanism to create a texture using a customizable TextureLoader class TextureSource { @@ -463,9 +463,9 @@ protected: gpu::TexturePointer _gpuTexture; QUrl _imageUrl; }; -typedef std::shared_ptr< TextureSource > TextureSourcePointer; - -}; - - -#endif +typedef std::shared_ptr< TextureSource > TextureSourcePointer; + +}; + + +#endif diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index b766cc88d4..e67fbf7f66 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -1,143 +1,143 @@ - -<@if not GPU_TRANSFORM_STATE_SLH@> -<@def GPU_TRANSFORM_STATE_SLH@> - -<@func declareStandardTransform()@> -struct TransformObject { - mat4 _model; - mat4 _modelInverse; -}; - -struct TransformCamera { - mat4 _view; - mat4 _viewInverse; - mat4 _projectionViewUntranslated; - mat4 _projection; - mat4 _projectionInverse; - vec4 _viewport; -}; - -layout(std140) uniform transformObjectBuffer { - TransformObject _object; -}; -TransformObject getTransformObject() { - return _object; -} - -layout(std140) uniform transformCameraBuffer { - TransformCamera _camera; -}; -TransformCamera getTransformCamera() { - return _camera; -} -<@endfunc@> - -<@func transformCameraViewport(cameraTransform, viewport)@> - <$viewport$> = <$cameraTransform$>._viewport; -<@endfunc@> - -<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> - - { // transformModelToClipPos - vec4 _eyepos = (<$objectTransform$>._model * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); - <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; - } -<@endfunc@> - -<@func transformInstancedModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> - - { // transformModelToClipPos - vec4 _eyepos = (inInstanceTransform * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); - <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; - } -<@endfunc@> - -<@func $transformModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@> - - { // transformModelToClipPos - vec4 _worldpos = (<$objectTransform$>._model * <$modelPos$>); - <$eyePos$> = (<$cameraTransform$>._view * _worldpos); - vec4 _eyepos =(<$objectTransform$>._model * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); - <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; - // <$eyePos$> = (<$cameraTransform$>._projectionInverse * <$clipPos$>); - } -<@endfunc@> - -<@func $transformInstancedModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@> - - { // transformModelToClipPos - vec4 _worldpos = (inInstanceTransform * <$modelPos$>); - <$eyePos$> = (<$cameraTransform$>._view * _worldpos); - vec4 _eyepos =(inInstanceTransform * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); - <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; - // <$eyePos$> = (<$cameraTransform$>._projectionInverse * <$clipPos$>); - } -<@endfunc@> - - -<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@> - { // transformModelToWorldPos - <$worldPos$> = (<$objectTransform$>._model * <$modelPos$>); - } -<@endfunc@> - -<@func transformInstancedModelToWorldPos(objectTransform, modelPos, worldPos)@> - { // transformModelToWorldPos - <$worldPos$> = (inInstanceTransform * <$modelPos$>); - } -<@endfunc@> - -<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@> - { // transformModelToEyeDir - vec3 mr0 = vec3(<$objectTransform$>._modelInverse[0].x, <$objectTransform$>._modelInverse[1].x, <$objectTransform$>._modelInverse[2].x); - vec3 mr1 = vec3(<$objectTransform$>._modelInverse[0].y, <$objectTransform$>._modelInverse[1].y, <$objectTransform$>._modelInverse[2].y); - vec3 mr2 = vec3(<$objectTransform$>._modelInverse[0].z, <$objectTransform$>._modelInverse[1].z, <$objectTransform$>._modelInverse[2].z); - - vec3 mvc0 = vec3(dot(<$cameraTransform$>._viewInverse[0].xyz, mr0), dot(<$cameraTransform$>._viewInverse[0].xyz, mr1), dot(<$cameraTransform$>._viewInverse[0].xyz, mr2)); - vec3 mvc1 = vec3(dot(<$cameraTransform$>._viewInverse[1].xyz, mr0), dot(<$cameraTransform$>._viewInverse[1].xyz, mr1), dot(<$cameraTransform$>._viewInverse[1].xyz, mr2)); - vec3 mvc2 = vec3(dot(<$cameraTransform$>._viewInverse[2].xyz, mr0), dot(<$cameraTransform$>._viewInverse[2].xyz, mr1), dot(<$cameraTransform$>._viewInverse[2].xyz, mr2)); - - <$eyeDir$> = vec3(dot(mvc0, <$modelDir$>), dot(mvc1, <$modelDir$>), dot(mvc2, <$modelDir$>)); - } -<@endfunc@> - -<@func transformInstancedModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@> - { // transformModelToEyeDir - mat4 modelInverse = inverse(inInstanceTransform); - vec3 mr0 = vec3(modelInverse[0].x, modelInverse[1].x, modelInverse[2].x); - vec3 mr1 = vec3(modelInverse[0].y, modelInverse[1].y, modelInverse[2].y); - vec3 mr2 = vec3(modelInverse[0].z, modelInverse[1].z, modelInverse[2].z); - - vec3 mvc0 = vec3(dot(<$cameraTransform$>._viewInverse[0].xyz, mr0), dot(<$cameraTransform$>._viewInverse[0].xyz, mr1), dot(<$cameraTransform$>._viewInverse[0].xyz, mr2)); - vec3 mvc1 = vec3(dot(<$cameraTransform$>._viewInverse[1].xyz, mr0), dot(<$cameraTransform$>._viewInverse[1].xyz, mr1), dot(<$cameraTransform$>._viewInverse[1].xyz, mr2)); - vec3 mvc2 = vec3(dot(<$cameraTransform$>._viewInverse[2].xyz, mr0), dot(<$cameraTransform$>._viewInverse[2].xyz, mr1), dot(<$cameraTransform$>._viewInverse[2].xyz, mr2)); - - <$eyeDir$> = vec3(dot(mvc0, <$modelDir$>), dot(mvc1, <$modelDir$>), dot(mvc2, <$modelDir$>)); - } -<@endfunc@> - -<@func transformEyeToWorldDir(cameraTransform, eyeDir, worldDir)@> - { // transformEyeToWorldDir - <$worldDir$> = vec3(<$cameraTransform$>._viewInverse * vec4(<$eyeDir$>.xyz, 0.0)); - } -<@endfunc@> - -<@func transformClipToEyeDir(cameraTransform, clipPos, eyeDir)@> - { // transformClipToEyeDir - <$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0)); - } -<@endfunc@> - -<@endif@> + +<@if not GPU_TRANSFORM_STATE_SLH@> +<@def GPU_TRANSFORM_STATE_SLH@> + +<@func declareStandardTransform()@> +struct TransformObject { + mat4 _model; + mat4 _modelInverse; +}; + +struct TransformCamera { + mat4 _view; + mat4 _viewInverse; + mat4 _projectionViewUntranslated; + mat4 _projection; + mat4 _projectionInverse; + vec4 _viewport; +}; + +layout(std140) uniform transformObjectBuffer { + TransformObject _object; +}; +TransformObject getTransformObject() { + return _object; +} + +layout(std140) uniform transformCameraBuffer { + TransformCamera _camera; +}; +TransformCamera getTransformCamera() { + return _camera; +} +<@endfunc@> + +<@func transformCameraViewport(cameraTransform, viewport)@> + <$viewport$> = <$cameraTransform$>._viewport; +<@endfunc@> + +<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> + + { // transformModelToClipPos + vec4 _eyepos = (<$objectTransform$>._model * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); + <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; + } +<@endfunc@> + +<@func transformInstancedModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> + + { // transformModelToClipPos + vec4 _eyepos = (inInstanceTransform * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); + <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; + } +<@endfunc@> + +<@func $transformModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@> + + { // transformModelToClipPos + vec4 _worldpos = (<$objectTransform$>._model * <$modelPos$>); + <$eyePos$> = (<$cameraTransform$>._view * _worldpos); + vec4 _eyepos =(<$objectTransform$>._model * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); + <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; + // <$eyePos$> = (<$cameraTransform$>._projectionInverse * <$clipPos$>); + } +<@endfunc@> + +<@func $transformInstancedModelToEyeAndClipPos(cameraTransform, objectTransform, modelPos, eyePos, clipPos)@> + + { // transformModelToClipPos + vec4 _worldpos = (inInstanceTransform * <$modelPos$>); + <$eyePos$> = (<$cameraTransform$>._view * _worldpos); + vec4 _eyepos =(inInstanceTransform * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0); + <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos; + // <$eyePos$> = (<$cameraTransform$>._projectionInverse * <$clipPos$>); + } +<@endfunc@> + + +<@func transformModelToWorldPos(objectTransform, modelPos, worldPos)@> + { // transformModelToWorldPos + <$worldPos$> = (<$objectTransform$>._model * <$modelPos$>); + } +<@endfunc@> + +<@func transformInstancedModelToWorldPos(objectTransform, modelPos, worldPos)@> + { // transformModelToWorldPos + <$worldPos$> = (inInstanceTransform * <$modelPos$>); + } +<@endfunc@> + +<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@> + { // transformModelToEyeDir + vec3 mr0 = vec3(<$objectTransform$>._modelInverse[0].x, <$objectTransform$>._modelInverse[1].x, <$objectTransform$>._modelInverse[2].x); + vec3 mr1 = vec3(<$objectTransform$>._modelInverse[0].y, <$objectTransform$>._modelInverse[1].y, <$objectTransform$>._modelInverse[2].y); + vec3 mr2 = vec3(<$objectTransform$>._modelInverse[0].z, <$objectTransform$>._modelInverse[1].z, <$objectTransform$>._modelInverse[2].z); + + vec3 mvc0 = vec3(dot(<$cameraTransform$>._viewInverse[0].xyz, mr0), dot(<$cameraTransform$>._viewInverse[0].xyz, mr1), dot(<$cameraTransform$>._viewInverse[0].xyz, mr2)); + vec3 mvc1 = vec3(dot(<$cameraTransform$>._viewInverse[1].xyz, mr0), dot(<$cameraTransform$>._viewInverse[1].xyz, mr1), dot(<$cameraTransform$>._viewInverse[1].xyz, mr2)); + vec3 mvc2 = vec3(dot(<$cameraTransform$>._viewInverse[2].xyz, mr0), dot(<$cameraTransform$>._viewInverse[2].xyz, mr1), dot(<$cameraTransform$>._viewInverse[2].xyz, mr2)); + + <$eyeDir$> = vec3(dot(mvc0, <$modelDir$>), dot(mvc1, <$modelDir$>), dot(mvc2, <$modelDir$>)); + } +<@endfunc@> + +<@func transformInstancedModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@> + { // transformModelToEyeDir + mat4 modelInverse = inverse(inInstanceTransform); + vec3 mr0 = vec3(modelInverse[0].x, modelInverse[1].x, modelInverse[2].x); + vec3 mr1 = vec3(modelInverse[0].y, modelInverse[1].y, modelInverse[2].y); + vec3 mr2 = vec3(modelInverse[0].z, modelInverse[1].z, modelInverse[2].z); + + vec3 mvc0 = vec3(dot(<$cameraTransform$>._viewInverse[0].xyz, mr0), dot(<$cameraTransform$>._viewInverse[0].xyz, mr1), dot(<$cameraTransform$>._viewInverse[0].xyz, mr2)); + vec3 mvc1 = vec3(dot(<$cameraTransform$>._viewInverse[1].xyz, mr0), dot(<$cameraTransform$>._viewInverse[1].xyz, mr1), dot(<$cameraTransform$>._viewInverse[1].xyz, mr2)); + vec3 mvc2 = vec3(dot(<$cameraTransform$>._viewInverse[2].xyz, mr0), dot(<$cameraTransform$>._viewInverse[2].xyz, mr1), dot(<$cameraTransform$>._viewInverse[2].xyz, mr2)); + + <$eyeDir$> = vec3(dot(mvc0, <$modelDir$>), dot(mvc1, <$modelDir$>), dot(mvc2, <$modelDir$>)); + } +<@endfunc@> + +<@func transformEyeToWorldDir(cameraTransform, eyeDir, worldDir)@> + { // transformEyeToWorldDir + <$worldDir$> = vec3(<$cameraTransform$>._viewInverse * vec4(<$eyeDir$>.xyz, 0.0)); + } +<@endfunc@> + +<@func transformClipToEyeDir(cameraTransform, clipPos, eyeDir)@> + { // transformClipToEyeDir + <$eyeDir$> = vec3(<$cameraTransform$>._projectionInverse * vec4(<$clipPos$>.xyz, 1.0)); + } +<@endfunc@> + +<@endif@> diff --git a/libraries/model/src/model/Atmosphere.slh b/libraries/model/src/model/Atmosphere.slh index 0af5fa27f3..b8f390de0a 100755 --- a/libraries/model/src/model/Atmosphere.slh +++ b/libraries/model/src/model/Atmosphere.slh @@ -1,245 +1,245 @@ - -<@if not MODEL_ATMOSPHERE_SLH@> -<@def MODEL_ATMOSPHERE_SLH@> - - - -struct Atmosphere { - vec4 _invWaveLength; - vec4 _radiuses; - vec4 _scales; - vec4 _scatterings; - vec4 _control; -}; - -const int numSamples = 2; - -vec3 getAtmosphereInvWaveLength(Atmosphere a) { return a._invWaveLength.xyz; } // 1 / pow(wavelength, 4) for the red, green, and blue channels - -float getAtmosphereInnerRadius(Atmosphere a) { return a._radiuses.x; } // The inner (planetary) radius -float getAtmosphereOuterRadius(Atmosphere a) { return a._radiuses.y; } // The outer (atmosphere) radius - -float getAtmosphereScale(Atmosphere a) { return a._scales.x; } // 1 / (outerRadius - innerRadius) -float getAtmosphereScaleDepth(Atmosphere a) { return a._scales.y; } // The scale depth (i.e. the altitude at which the atmosphere's average density is found) -float getAtmosphereScaleOverScaleDepth(Atmosphere a) { return a._scales.z; } // scale / scaleDepth - -vec4 getAtmosphereScattering(Atmosphere a) { return a._scatterings; } // The full Mie and Rayleigh scattering coefficients -float getAtmosphereKrESun(Atmosphere a) { return a._scatterings.x; } // Kr * ESun -float getAtmosphereKmESun(Atmosphere a) { return a._scatterings.y; } // Km * ESun -float getAtmosphereKr4PI(Atmosphere a) { return a._scatterings.z; } // Kr * 4 * PI -float getAtmosphereKm4PI(Atmosphere a) { return a._scatterings.w; } // Km * 4 * PI - -float getAtmosphereNumSamples(Atmosphere a) { return a._control.x; } // numSamples -vec2 getAtmosphereGAndG2(Atmosphere a) { return a._control.yz; } // g and g2 - -float atmosphereScale(float scaleDepth, float fCos) -{ - float x = 1.0 - fCos; - return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); -} - -vec4 evalAtmosphereContribution(Atmosphere atmospheric, vec3 position, vec3 cameraPos, vec3 lightPos) { - float fInnerRadius = getAtmosphereInnerRadius(atmospheric); - float fSamples = getAtmosphereNumSamples(atmospheric); - - vec3 v3InvWavelength = getAtmosphereInvWaveLength(atmospheric); - vec4 scatteringCoefs = getAtmosphereScattering(atmospheric); - float fKrESun = scatteringCoefs.x; - float fKmESun = scatteringCoefs.y; - float fKr4PI = scatteringCoefs.z; - float fKm4PI = scatteringCoefs.w; - - vec2 gAndg2 = getAtmosphereGAndG2(atmospheric); - float g = gAndg2.x; - float g2 = gAndg2.y; - - float fScale = getAtmosphereScale(atmospheric); - float fScaleDepth = getAtmosphereScaleDepth(atmospheric); - float fScaleOverScaleDepth = getAtmosphereScaleOverScaleDepth(atmospheric); - - // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) - vec3 v3Pos = position; - vec3 v3Ray = v3Pos - cameraPos; - float fFar = length(v3Ray); - v3Ray /= fFar; - - // Calculate the ray's starting position, then calculate its scattering offset - vec3 v3Start = cameraPos; - float fHeight = length(v3Start); - float fDepthStart = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); - float fStartAngle = dot(v3Ray, v3Start) / fHeight; - float fStartOffset = fDepthStart * atmosphereScale(fScaleDepth, fStartAngle); - - // Initialize the scattering loop variables - //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); - float fSampleLength = fFar / fSamples; - float fScaledLength = fSampleLength * fScale; - - vec3 v3SampleRay = v3Ray * fSampleLength; - vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; - - // Now loop through the sample rays - vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); - // int nSamples = numSamples; - int nSamples = int(fSamples); - for(int i=0; i -uniform atmosphereBuffer { - Atmosphere _atmosphere; -}; -Atmosphere getAtmosphere() { - return _atmosphere; -} -<@else@> -uniform vec4 atmosphereBuffer[9]; -Atmosphere getAtmosphere() { - Atmosphere atmosphere; - atmosphere._invWaveLength = atmosphereBuffer[0]; - atmosphere._radiuses = atmosphereBuffer[1]; - atmosphere._scales = atmosphereBuffer[2]; - atmosphere._scatterings = atmosphereBuffer[3]; - atmosphere._control = atmosphereBuffer[4]; - - return atmosphere; -} -<@endif@> - - - -<@endif@> + +<@if not MODEL_ATMOSPHERE_SLH@> +<@def MODEL_ATMOSPHERE_SLH@> + + + +struct Atmosphere { + vec4 _invWaveLength; + vec4 _radiuses; + vec4 _scales; + vec4 _scatterings; + vec4 _control; +}; + +const int numSamples = 2; + +vec3 getAtmosphereInvWaveLength(Atmosphere a) { return a._invWaveLength.xyz; } // 1 / pow(wavelength, 4) for the red, green, and blue channels + +float getAtmosphereInnerRadius(Atmosphere a) { return a._radiuses.x; } // The inner (planetary) radius +float getAtmosphereOuterRadius(Atmosphere a) { return a._radiuses.y; } // The outer (atmosphere) radius + +float getAtmosphereScale(Atmosphere a) { return a._scales.x; } // 1 / (outerRadius - innerRadius) +float getAtmosphereScaleDepth(Atmosphere a) { return a._scales.y; } // The scale depth (i.e. the altitude at which the atmosphere's average density is found) +float getAtmosphereScaleOverScaleDepth(Atmosphere a) { return a._scales.z; } // scale / scaleDepth + +vec4 getAtmosphereScattering(Atmosphere a) { return a._scatterings; } // The full Mie and Rayleigh scattering coefficients +float getAtmosphereKrESun(Atmosphere a) { return a._scatterings.x; } // Kr * ESun +float getAtmosphereKmESun(Atmosphere a) { return a._scatterings.y; } // Km * ESun +float getAtmosphereKr4PI(Atmosphere a) { return a._scatterings.z; } // Kr * 4 * PI +float getAtmosphereKm4PI(Atmosphere a) { return a._scatterings.w; } // Km * 4 * PI + +float getAtmosphereNumSamples(Atmosphere a) { return a._control.x; } // numSamples +vec2 getAtmosphereGAndG2(Atmosphere a) { return a._control.yz; } // g and g2 + +float atmosphereScale(float scaleDepth, float fCos) +{ + float x = 1.0 - fCos; + return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); +} + +vec4 evalAtmosphereContribution(Atmosphere atmospheric, vec3 position, vec3 cameraPos, vec3 lightPos) { + float fInnerRadius = getAtmosphereInnerRadius(atmospheric); + float fSamples = getAtmosphereNumSamples(atmospheric); + + vec3 v3InvWavelength = getAtmosphereInvWaveLength(atmospheric); + vec4 scatteringCoefs = getAtmosphereScattering(atmospheric); + float fKrESun = scatteringCoefs.x; + float fKmESun = scatteringCoefs.y; + float fKr4PI = scatteringCoefs.z; + float fKm4PI = scatteringCoefs.w; + + vec2 gAndg2 = getAtmosphereGAndG2(atmospheric); + float g = gAndg2.x; + float g2 = gAndg2.y; + + float fScale = getAtmosphereScale(atmospheric); + float fScaleDepth = getAtmosphereScaleDepth(atmospheric); + float fScaleOverScaleDepth = getAtmosphereScaleOverScaleDepth(atmospheric); + + // Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere) + vec3 v3Pos = position; + vec3 v3Ray = v3Pos - cameraPos; + float fFar = length(v3Ray); + v3Ray /= fFar; + + // Calculate the ray's starting position, then calculate its scattering offset + vec3 v3Start = cameraPos; + float fHeight = length(v3Start); + float fDepthStart = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight)); + float fStartAngle = dot(v3Ray, v3Start) / fHeight; + float fStartOffset = fDepthStart * atmosphereScale(fScaleDepth, fStartAngle); + + // Initialize the scattering loop variables + //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0); + float fSampleLength = fFar / fSamples; + float fScaledLength = fSampleLength * fScale; + + vec3 v3SampleRay = v3Ray * fSampleLength; + vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + + // Now loop through the sample rays + vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); + // int nSamples = numSamples; + int nSamples = int(fSamples); + for(int i=0; i +uniform atmosphereBuffer { + Atmosphere _atmosphere; +}; +Atmosphere getAtmosphere() { + return _atmosphere; +} +<@else@> +uniform vec4 atmosphereBuffer[9]; +Atmosphere getAtmosphere() { + Atmosphere atmosphere; + atmosphere._invWaveLength = atmosphereBuffer[0]; + atmosphere._radiuses = atmosphereBuffer[1]; + atmosphere._scales = atmosphereBuffer[2]; + atmosphere._scatterings = atmosphereBuffer[3]; + atmosphere._control = atmosphereBuffer[4]; + + return atmosphere; +} +<@endif@> + + + +<@endif@> diff --git a/libraries/model/src/model/Light.h b/libraries/model/src/model/Light.h index 3fbaba75bf..efc02e3de4 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/model/src/model/Light.h @@ -77,13 +77,13 @@ public: void setMaximumRadius(float radius); float getMaximumRadius() const { return getSchema()._attenuation.w; } - // Spot properties - bool isSpot() const { return getType() == SPOT; } - void setSpotAngle(float angle); - float getSpotAngle() const { return getSchema()._spot.z; } - glm::vec2 getSpotAngleCosSin() const { return glm::vec2(getSchema()._spot.x, getSchema()._spot.y); } - void setSpotExponent(float exponent); - float getSpotExponent() const { return getSchema()._spot.w; } + // Spot properties + bool isSpot() const { return getType() == SPOT; } + void setSpotAngle(float angle); + float getSpotAngle() const { return getSchema()._spot.z; } + glm::vec2 getSpotAngleCosSin() const { return glm::vec2(getSchema()._spot.x, getSchema()._spot.y); } + void setSpotExponent(float exponent); + float getSpotExponent() const { return getSchema()._spot.w; } // For editing purpose, show the light volume contour. // Set to non 0 to show it, the value is used as the intensity of the contour color @@ -100,20 +100,20 @@ public: void setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset) { _ambientSphere.assignPreset(preset); } // Schema to access the attribute values of the light - class Schema { - public: - Vec4 _position{0.0f, 0.0f, 0.0f, 1.0f}; - Vec3 _direction{0.0f, 0.0f, -1.0f}; - float _ambientIntensity{0.0f}; - Color _color{1.0f}; - float _intensity{1.0f}; - Vec4 _attenuation{1.0f}; - Vec4 _spot{0.0f, 0.0f, 0.0f, 0.0f}; - Vec4 _shadow{0.0f}; - - Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f}; - - Schema() {} + class Schema { + public: + Vec4 _position{0.0f, 0.0f, 0.0f, 1.0f}; + Vec3 _direction{0.0f, 0.0f, -1.0f}; + float _ambientIntensity{0.0f}; + Color _color{1.0f}; + float _intensity{1.0f}; + Vec4 _attenuation{1.0f}; + Vec4 _spot{0.0f, 0.0f, 0.0f, 0.0f}; + Vec4 _shadow{0.0f}; + + Vec4 _control{0.0f, 0.0f, 0.0f, 0.0f}; + + Schema() {} }; const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; } diff --git a/libraries/model/src/model/Light.slh b/libraries/model/src/model/Light.slh index 1aaf0e8327..708e092a0a 100644 --- a/libraries/model/src/model/Light.slh +++ b/libraries/model/src/model/Light.slh @@ -1,93 +1,93 @@ - -<@if not MODEL_LIGHT_SLH@> -<@def MODEL_LIGHT_SLH@> - -struct Light { - vec4 _position; - vec4 _direction; - vec4 _color; - vec4 _attenuation; - vec4 _spot; - - vec4 _shadow; - vec4 _control; -}; - -vec3 getLightPosition(Light l) { return l._position.xyz; } -vec3 getLightDirection(Light l) { return l._direction.xyz; } // direction is -Z axis - -vec3 getLightColor(Light l) { return l._color.rgb; } -float getLightIntensity(Light l) { return l._color.w; } -float getLightAmbientIntensity(Light l) { return l._direction.w; } - -float evalLightAttenuation(Light l, float r) { - float d = max(r - l._attenuation.x, 0.0); - float denom = d * l._attenuation.y + 1.0; - float attenuation = 1.0 / (denom * denom); - return max((attenuation - l._attenuation.z)/(1.0 - l._attenuation.z), 0.0); - // return clamp(1.0/(l._attenuation.x + l._attenuation.y * r + l._attenuation.z * r * r), 0.0, 1.0); -} - -float getLightSpotAngleCos(Light l) { - return l._spot.x; -} - -vec2 getLightSpotOutsideNormal2(Light l) { - return vec2(-l._spot.y, l._spot.x); -} - -float evalLightSpotAttenuation(Light l, float cosA) { - return pow(cosA, l._spot.w); -} - -float getLightSquareRadius(Light l) { - return l._attenuation.w * l._attenuation.w; -} - -float getLightRadius(Light l) { - return l._attenuation.w; -} - -float getLightAttenuationCutoff(Light l) { - return l._attenuation.z; -} - -float getLightShowContour(Light l) { - return l._control.w; -} - -<@if GPU_FEATURE_PROFILE == GPU_CORE @> -uniform lightBuffer { - Light light; -}; -Light getLight() { - return light; -} -<@else@> -uniform vec4 lightBuffer[7]; -Light getLight() { - Light light; - light._position = lightBuffer[0]; - light._direction = lightBuffer[1]; - light._color = lightBuffer[2]; - light._attenuation = lightBuffer[3]; - light._spot = lightBuffer[4]; - light._shadow = lightBuffer[5]; - light._control = lightBuffer[6]; - - return light; -} -<@endif@> - - - -<@endif@> + +<@if not MODEL_LIGHT_SLH@> +<@def MODEL_LIGHT_SLH@> + +struct Light { + vec4 _position; + vec4 _direction; + vec4 _color; + vec4 _attenuation; + vec4 _spot; + + vec4 _shadow; + vec4 _control; +}; + +vec3 getLightPosition(Light l) { return l._position.xyz; } +vec3 getLightDirection(Light l) { return l._direction.xyz; } // direction is -Z axis + +vec3 getLightColor(Light l) { return l._color.rgb; } +float getLightIntensity(Light l) { return l._color.w; } +float getLightAmbientIntensity(Light l) { return l._direction.w; } + +float evalLightAttenuation(Light l, float r) { + float d = max(r - l._attenuation.x, 0.0); + float denom = d * l._attenuation.y + 1.0; + float attenuation = 1.0 / (denom * denom); + return max((attenuation - l._attenuation.z)/(1.0 - l._attenuation.z), 0.0); + // return clamp(1.0/(l._attenuation.x + l._attenuation.y * r + l._attenuation.z * r * r), 0.0, 1.0); +} + +float getLightSpotAngleCos(Light l) { + return l._spot.x; +} + +vec2 getLightSpotOutsideNormal2(Light l) { + return vec2(-l._spot.y, l._spot.x); +} + +float evalLightSpotAttenuation(Light l, float cosA) { + return pow(cosA, l._spot.w); +} + +float getLightSquareRadius(Light l) { + return l._attenuation.w * l._attenuation.w; +} + +float getLightRadius(Light l) { + return l._attenuation.w; +} + +float getLightAttenuationCutoff(Light l) { + return l._attenuation.z; +} + +float getLightShowContour(Light l) { + return l._control.w; +} + +<@if GPU_FEATURE_PROFILE == GPU_CORE @> +uniform lightBuffer { + Light light; +}; +Light getLight() { + return light; +} +<@else@> +uniform vec4 lightBuffer[7]; +Light getLight() { + Light light; + light._position = lightBuffer[0]; + light._direction = lightBuffer[1]; + light._color = lightBuffer[2]; + light._attenuation = lightBuffer[3]; + light._spot = lightBuffer[4]; + light._shadow = lightBuffer[5]; + light._control = lightBuffer[6]; + + return light; +} +<@endif@> + + + +<@endif@> diff --git a/libraries/model/src/model/Material.slh b/libraries/model/src/model/Material.slh index 35aa96042c..f48bdfa8f1 100644 --- a/libraries/model/src/model/Material.slh +++ b/libraries/model/src/model/Material.slh @@ -1,66 +1,66 @@ - -<@if not MODEL_MATERIAL_SLH@> -<@def MODEL_MATERIAL_SLH@> - -struct Material { - vec4 _diffuse; - vec4 _specular; - vec4 _emissive; - vec4 _spare; -}; - -uniform materialBuffer { - Material _mat; -}; - -Material getMaterial() { - return _mat; -} - - 0.04045 - // constants: - // T = 0.04045 - // A = 1 / 1.055 = 0.94786729857 - // B = 0.055 * A = 0.05213270142 - // C = 1 / 12.92 = 0.0773993808 - // G = 2.4 - const float T = 0.04045; - const float A = 0.947867; - const float B = 0.052132; - const float C = 0.077399; - const float G = 2.4; - - if (cs > T) { - return pow((cs * A + B), G); - } else { - return cs * C; - } -} - -vec3 SRGBToLinear(vec3 srgb) { - return vec3(componentSRGBToLinear(srgb.x),componentSRGBToLinear(srgb.y),componentSRGBToLinear(srgb.z)); -} -vec3 getMaterialDiffuse(Material m) { return (gl_FragCoord.x < 800 ? SRGBToLinear(m._diffuse.rgb) : m._diffuse.rgb); } -*/!> - -float getMaterialOpacity(Material m) { return m._diffuse.a; } -vec3 getMaterialDiffuse(Material m) { return m._diffuse.rgb; } -vec3 getMaterialSpecular(Material m) { return m._specular.rgb; } -float getMaterialShininess(Material m) { return m._specular.a; } - -<@endif@> + +<@if not MODEL_MATERIAL_SLH@> +<@def MODEL_MATERIAL_SLH@> + +struct Material { + vec4 _diffuse; + vec4 _specular; + vec4 _emissive; + vec4 _spare; +}; + +uniform materialBuffer { + Material _mat; +}; + +Material getMaterial() { + return _mat; +} + + 0.04045 + // constants: + // T = 0.04045 + // A = 1 / 1.055 = 0.94786729857 + // B = 0.055 * A = 0.05213270142 + // C = 1 / 12.92 = 0.0773993808 + // G = 2.4 + const float T = 0.04045; + const float A = 0.947867; + const float B = 0.052132; + const float C = 0.077399; + const float G = 2.4; + + if (cs > T) { + return pow((cs * A + B), G); + } else { + return cs * C; + } +} + +vec3 SRGBToLinear(vec3 srgb) { + return vec3(componentSRGBToLinear(srgb.x),componentSRGBToLinear(srgb.y),componentSRGBToLinear(srgb.z)); +} +vec3 getMaterialDiffuse(Material m) { return (gl_FragCoord.x < 800 ? SRGBToLinear(m._diffuse.rgb) : m._diffuse.rgb); } +*/!> + +float getMaterialOpacity(Material m) { return m._diffuse.a; } +vec3 getMaterialDiffuse(Material m) { return m._diffuse.rgb; } +vec3 getMaterialSpecular(Material m) { return m._specular.rgb; } +float getMaterialShininess(Material m) { return m._specular.a; } + +<@endif@> diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 386569dd08..67f0262b61 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -1,303 +1,303 @@ -// -// Stage.cpp -// libraries/model/src/model -// -// Created by Sam Gateau on 2/24/2015. -// 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 "Stage.h" - -#include -#include -#include - -using namespace model; - - -void EarthSunModel::updateAll() const { - updateWorldToSurface(); - updateSurfaceToEye(); - updateSun(); -} - -Mat4d EarthSunModel::evalWorldToGeoLocationMat(double longitude, double latitude, double absAltitude, double scale) { - // Longitude is along Z axis but - from east to west - Mat4d rotLon = glm::rotate(glm::radians(longitude), Vec3d(0.0, 0.0, 1.0)); - - // latitude is along X axis + from south to north - Mat4d rotLat = glm::rotate(-glm::radians(latitude), Vec3d(1.0, 0.0, 0.0)); - - // translation is movin to the earth surface + altiture at the radius along Y axis - Mat4d surfaceT = glm::translate(Vec3d(0.0, -absAltitude, 0.0)); - - // Mat4d worldScale = glm::scale(Vec3d(scale)); - - Mat4d worldToGeoLocMat = surfaceT * rotLat * rotLon; - - return worldToGeoLocMat; -} - -void EarthSunModel::updateWorldToSurface() const { - // Check if the final position is too close to the earth center ? - float absAltitude = _earthRadius + _altitude; - if ( absAltitude < 0.01f) { - absAltitude = 0.01f; - } - - // Final world to local Frame - _worldToSurfaceMat = evalWorldToGeoLocationMat(_longitude, _latitude, absAltitude, _scale); - // and the inverse - _surfaceToWorldMat = glm::inverse(_worldToSurfaceMat); - - _surfacePos = Vec3d(_surfaceToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0)); -} - -void EarthSunModel::updateSurfaceToEye() const { - _surfaceToEyeMat = glm::inverse(_eyeToSurfaceMat); - _worldToEyeMat = _surfaceToEyeMat * _worldToSurfaceMat; - _eyeToWorldMat = _surfaceToWorldMat * _eyeToSurfaceMat; - _eyePos = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0) ); - _eyeDir = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, -1.0, 0.0) ); -} - -void EarthSunModel::updateSun() const { - // Longitude is along Y axis but - from east to west - Mat4d rotSunLon; - - Mat4d rotSun = evalWorldToGeoLocationMat(_sunLongitude, _sunLatitude, _earthRadius, _scale); - rotSun = glm::inverse(rotSun); - - _sunDir = Vec3d(rotSun * Vec4d(0.0, 1.0, 0.0, 0.0)); - - // sun direction is looking up toward Y axis at the specified sun lat, long - Vec3d lssd = Vec3d(_worldToSurfaceMat * Vec4d(_sunDir.x, _sunDir.y, _sunDir.z, 0.0)); - - // apply surface rotation offset - glm::dquat dSurfOrient(_surfaceOrientation); - lssd = glm::rotate(dSurfOrient, lssd); - - _surfaceSunDir = glm::normalize(Vec3(lssd.x, lssd.y, lssd.z)); -} - -void EarthSunModel::setSurfaceOrientation(const Quat& orientation) { - _surfaceOrientation = orientation; - invalidate(); -} - -double moduloRange(double val, double minVal, double maxVal) { - double range = maxVal - minVal; - double rval = (val - minVal) / range; - rval = rval - floor(rval); - return rval * range + minVal; -} - -const float MAX_LONGITUDE = 180.0f; -const float MAX_LATITUDE = 90.0f; - -float validateLongitude(float lon) { - return moduloRange(lon, -MAX_LONGITUDE, MAX_LONGITUDE); -} - -float validateLatitude(float lat) { - return moduloRange(lat, -MAX_LATITUDE, MAX_LATITUDE); -} - -float validateAltitude(float altitude) { - const float MIN_ALTITUDE = -1000.0f; - const float MAX_ALTITUDE = 100000.0f; - return std::min(std::max(altitude, MIN_ALTITUDE), MAX_ALTITUDE); -} - -void EarthSunModel::setLatitude(float lat) { - _latitude = validateLatitude(lat); - invalidate(); -} -void EarthSunModel::setLongitude(float lon) { - _longitude = validateLongitude(lon); - invalidate(); -} -void EarthSunModel::setAltitude(float altitude) { - _altitude = validateAltitude(altitude); - invalidate(); -} - -void EarthSunModel::setSunLatitude(float lat) { - _sunLatitude = validateLatitude(lat); - invalidate(); -} -void EarthSunModel::setSunLongitude(float lon) { - _sunLongitude = validateLongitude(lon); - invalidate(); -} - -Atmosphere::Atmosphere() { - // only if created from nothing shall we create the Buffer to store the properties - Data data; - _dataBuffer = gpu::BufferView(std::make_shared(sizeof(Data), (const gpu::Byte*) &data)); - - setScatteringWavelength(_scatteringWavelength); - setRayleighScattering(_rayleighScattering); - setInnerOuterRadiuses(getInnerRadius(), getOuterRadius()); -} - -void Atmosphere::setScatteringWavelength(Vec3 wavelength) { - _scatteringWavelength = wavelength; - Data& data = editData(); - data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); -} - -void Atmosphere::setRayleighScattering(float scattering) { - _rayleighScattering = scattering; - updateScattering(); -} - -void Atmosphere::setMieScattering(float scattering) { - _mieScattering = scattering; - updateScattering(); -} - -void Atmosphere::setSunBrightness(float brightness) { - _sunBrightness = brightness; - updateScattering(); -} - -void Atmosphere::updateScattering() { - Data& data = editData(); - - data._scatterings.x = getRayleighScattering() * getSunBrightness(); - data._scatterings.y = getMieScattering() * getSunBrightness(); - - data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); - data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); -} - -void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { - Data& data = editData(); - data._radiuses.x = inner; - data._radiuses.y = outer; - data._scales.x = 1.0f / (outer - inner); - data._scales.z = data._scales.x / data._scales.y; -} - - -const int NUM_DAYS_PER_YEAR = 365; -const float NUM_HOURS_PER_DAY = 24.0f; -const float NUM_HOURS_PER_HALF_DAY = NUM_HOURS_PER_DAY * 0.5f; - -SunSkyStage::SunSkyStage() : - _sunLight(std::make_shared()), - _skybox(std::make_shared()) -{ - _sunLight->setType(Light::SUN); - - setSunIntensity(1.0f); - setSunAmbientIntensity(0.5f); - setSunColor(Vec3(1.0f, 1.0f, 1.0f)); - - // Default origin location is a special place in the world... - setOriginLocation(122.407f, 37.777f, 0.03f); - // Noun - setDayTime(12.0f); - // Begining of march - setYearTime(60.0f); - - _skybox->setColor(Color(1.0f, 0.0f, 0.0f)); -} - -SunSkyStage::~SunSkyStage() { -} - -void SunSkyStage::setDayTime(float hour) { - _dayTime = moduloRange(hour, 0.f, NUM_HOURS_PER_DAY); - invalidate(); -} - -void SunSkyStage::setYearTime(unsigned int day) { - _yearTime = day % NUM_DAYS_PER_YEAR; - invalidate(); -} - -void SunSkyStage::setOriginOrientation(const Quat& orientation) { - _earthSunModel.setSurfaceOrientation(orientation); - invalidate(); -} - -void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { - _earthSunModel.setLongitude(longitude); - _earthSunModel.setLatitude(latitude); - _earthSunModel.setAltitude(altitude); - invalidate(); -} - -void SunSkyStage::setSunModelEnable(bool isEnabled) { - _sunModelEnable = isEnabled; - invalidate(); -} - -void SunSkyStage::setSunColor(const Vec3& color) { - _sunLight->setColor(color); -} -void SunSkyStage::setSunIntensity(float intensity) { - _sunLight->setIntensity(intensity); -} -void SunSkyStage::setSunAmbientIntensity(float intensity) { - _sunLight->setAmbientIntensity(intensity); -} - -void SunSkyStage::setSunDirection(const Vec3& direction) { - if (!isSunModelEnabled()) { - _sunLight->setDirection(direction); - } -} - -// THe sun declinaison calculus is taken from https://en.wikipedia.org/wiki/Position_of_the_Sun -double evalSunDeclinaison(double dayNumber) { - return -(23.0 + 44.0/60.0)*cos(glm::radians((360.0/365.0)*(dayNumber + 10.0))); -} - -void SunSkyStage::updateGraphicsObject() const { - // Always update the sunLongitude based on the current dayTime and the current origin - // The day time is supposed to be local at the origin - float signedNormalizedDayTime = (_dayTime - NUM_HOURS_PER_HALF_DAY) / NUM_HOURS_PER_HALF_DAY; - float sunLongitude = _earthSunModel.getLongitude() + (MAX_LONGITUDE * signedNormalizedDayTime); - _earthSunModel.setSunLongitude(sunLongitude); - - // And update the sunLAtitude as the declinaison depending of the time of the year - _earthSunModel.setSunLatitude(evalSunDeclinaison(_yearTime)); - - if (isSunModelEnabled()) { - Vec3d sunLightDir = -_earthSunModel.getSurfaceSunDir(); - _sunLight->setDirection(Vec3(sunLightDir.x, sunLightDir.y, sunLightDir.z)); - - double originAlt = _earthSunModel.getAltitude(); - _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); - } - - // Background - switch (getBackgroundMode()) { - case NO_BACKGROUND: { - break; - } - case SKY_DOME: { - break; - } - case SKY_BOX: { - break; - } - case NUM_BACKGROUND_MODES: - Q_UNREACHABLE(); - }; -} - -void SunSkyStage::setBackgroundMode(BackgroundMode mode) { - _backgroundMode = mode; - invalidate(); -} - -void SunSkyStage::setSkybox(const SkyboxPointer& skybox) { - _skybox = skybox; - invalidate(); -} +// +// Stage.cpp +// libraries/model/src/model +// +// Created by Sam Gateau on 2/24/2015. +// 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 "Stage.h" + +#include +#include +#include + +using namespace model; + + +void EarthSunModel::updateAll() const { + updateWorldToSurface(); + updateSurfaceToEye(); + updateSun(); +} + +Mat4d EarthSunModel::evalWorldToGeoLocationMat(double longitude, double latitude, double absAltitude, double scale) { + // Longitude is along Z axis but - from east to west + Mat4d rotLon = glm::rotate(glm::radians(longitude), Vec3d(0.0, 0.0, 1.0)); + + // latitude is along X axis + from south to north + Mat4d rotLat = glm::rotate(-glm::radians(latitude), Vec3d(1.0, 0.0, 0.0)); + + // translation is movin to the earth surface + altiture at the radius along Y axis + Mat4d surfaceT = glm::translate(Vec3d(0.0, -absAltitude, 0.0)); + + // Mat4d worldScale = glm::scale(Vec3d(scale)); + + Mat4d worldToGeoLocMat = surfaceT * rotLat * rotLon; + + return worldToGeoLocMat; +} + +void EarthSunModel::updateWorldToSurface() const { + // Check if the final position is too close to the earth center ? + float absAltitude = _earthRadius + _altitude; + if ( absAltitude < 0.01f) { + absAltitude = 0.01f; + } + + // Final world to local Frame + _worldToSurfaceMat = evalWorldToGeoLocationMat(_longitude, _latitude, absAltitude, _scale); + // and the inverse + _surfaceToWorldMat = glm::inverse(_worldToSurfaceMat); + + _surfacePos = Vec3d(_surfaceToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0)); +} + +void EarthSunModel::updateSurfaceToEye() const { + _surfaceToEyeMat = glm::inverse(_eyeToSurfaceMat); + _worldToEyeMat = _surfaceToEyeMat * _worldToSurfaceMat; + _eyeToWorldMat = _surfaceToWorldMat * _eyeToSurfaceMat; + _eyePos = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, 0.0, 1.0) ); + _eyeDir = Vec3d(_eyeToWorldMat * Vec4d(0.0, 0.0, -1.0, 0.0) ); +} + +void EarthSunModel::updateSun() const { + // Longitude is along Y axis but - from east to west + Mat4d rotSunLon; + + Mat4d rotSun = evalWorldToGeoLocationMat(_sunLongitude, _sunLatitude, _earthRadius, _scale); + rotSun = glm::inverse(rotSun); + + _sunDir = Vec3d(rotSun * Vec4d(0.0, 1.0, 0.0, 0.0)); + + // sun direction is looking up toward Y axis at the specified sun lat, long + Vec3d lssd = Vec3d(_worldToSurfaceMat * Vec4d(_sunDir.x, _sunDir.y, _sunDir.z, 0.0)); + + // apply surface rotation offset + glm::dquat dSurfOrient(_surfaceOrientation); + lssd = glm::rotate(dSurfOrient, lssd); + + _surfaceSunDir = glm::normalize(Vec3(lssd.x, lssd.y, lssd.z)); +} + +void EarthSunModel::setSurfaceOrientation(const Quat& orientation) { + _surfaceOrientation = orientation; + invalidate(); +} + +double moduloRange(double val, double minVal, double maxVal) { + double range = maxVal - minVal; + double rval = (val - minVal) / range; + rval = rval - floor(rval); + return rval * range + minVal; +} + +const float MAX_LONGITUDE = 180.0f; +const float MAX_LATITUDE = 90.0f; + +float validateLongitude(float lon) { + return moduloRange(lon, -MAX_LONGITUDE, MAX_LONGITUDE); +} + +float validateLatitude(float lat) { + return moduloRange(lat, -MAX_LATITUDE, MAX_LATITUDE); +} + +float validateAltitude(float altitude) { + const float MIN_ALTITUDE = -1000.0f; + const float MAX_ALTITUDE = 100000.0f; + return std::min(std::max(altitude, MIN_ALTITUDE), MAX_ALTITUDE); +} + +void EarthSunModel::setLatitude(float lat) { + _latitude = validateLatitude(lat); + invalidate(); +} +void EarthSunModel::setLongitude(float lon) { + _longitude = validateLongitude(lon); + invalidate(); +} +void EarthSunModel::setAltitude(float altitude) { + _altitude = validateAltitude(altitude); + invalidate(); +} + +void EarthSunModel::setSunLatitude(float lat) { + _sunLatitude = validateLatitude(lat); + invalidate(); +} +void EarthSunModel::setSunLongitude(float lon) { + _sunLongitude = validateLongitude(lon); + invalidate(); +} + +Atmosphere::Atmosphere() { + // only if created from nothing shall we create the Buffer to store the properties + Data data; + _dataBuffer = gpu::BufferView(std::make_shared(sizeof(Data), (const gpu::Byte*) &data)); + + setScatteringWavelength(_scatteringWavelength); + setRayleighScattering(_rayleighScattering); + setInnerOuterRadiuses(getInnerRadius(), getOuterRadius()); +} + +void Atmosphere::setScatteringWavelength(Vec3 wavelength) { + _scatteringWavelength = wavelength; + Data& data = editData(); + data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f); +} + +void Atmosphere::setRayleighScattering(float scattering) { + _rayleighScattering = scattering; + updateScattering(); +} + +void Atmosphere::setMieScattering(float scattering) { + _mieScattering = scattering; + updateScattering(); +} + +void Atmosphere::setSunBrightness(float brightness) { + _sunBrightness = brightness; + updateScattering(); +} + +void Atmosphere::updateScattering() { + Data& data = editData(); + + data._scatterings.x = getRayleighScattering() * getSunBrightness(); + data._scatterings.y = getMieScattering() * getSunBrightness(); + + data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi(); + data._scatterings.w = getMieScattering() * 4.0f * glm::pi(); +} + +void Atmosphere::setInnerOuterRadiuses(float inner, float outer) { + Data& data = editData(); + data._radiuses.x = inner; + data._radiuses.y = outer; + data._scales.x = 1.0f / (outer - inner); + data._scales.z = data._scales.x / data._scales.y; +} + + +const int NUM_DAYS_PER_YEAR = 365; +const float NUM_HOURS_PER_DAY = 24.0f; +const float NUM_HOURS_PER_HALF_DAY = NUM_HOURS_PER_DAY * 0.5f; + +SunSkyStage::SunSkyStage() : + _sunLight(std::make_shared()), + _skybox(std::make_shared()) +{ + _sunLight->setType(Light::SUN); + + setSunIntensity(1.0f); + setSunAmbientIntensity(0.5f); + setSunColor(Vec3(1.0f, 1.0f, 1.0f)); + + // Default origin location is a special place in the world... + setOriginLocation(122.407f, 37.777f, 0.03f); + // Noun + setDayTime(12.0f); + // Begining of march + setYearTime(60.0f); + + _skybox->setColor(Color(1.0f, 0.0f, 0.0f)); +} + +SunSkyStage::~SunSkyStage() { +} + +void SunSkyStage::setDayTime(float hour) { + _dayTime = moduloRange(hour, 0.f, NUM_HOURS_PER_DAY); + invalidate(); +} + +void SunSkyStage::setYearTime(unsigned int day) { + _yearTime = day % NUM_DAYS_PER_YEAR; + invalidate(); +} + +void SunSkyStage::setOriginOrientation(const Quat& orientation) { + _earthSunModel.setSurfaceOrientation(orientation); + invalidate(); +} + +void SunSkyStage::setOriginLocation(float longitude, float latitude, float altitude) { + _earthSunModel.setLongitude(longitude); + _earthSunModel.setLatitude(latitude); + _earthSunModel.setAltitude(altitude); + invalidate(); +} + +void SunSkyStage::setSunModelEnable(bool isEnabled) { + _sunModelEnable = isEnabled; + invalidate(); +} + +void SunSkyStage::setSunColor(const Vec3& color) { + _sunLight->setColor(color); +} +void SunSkyStage::setSunIntensity(float intensity) { + _sunLight->setIntensity(intensity); +} +void SunSkyStage::setSunAmbientIntensity(float intensity) { + _sunLight->setAmbientIntensity(intensity); +} + +void SunSkyStage::setSunDirection(const Vec3& direction) { + if (!isSunModelEnabled()) { + _sunLight->setDirection(direction); + } +} + +// THe sun declinaison calculus is taken from https://en.wikipedia.org/wiki/Position_of_the_Sun +double evalSunDeclinaison(double dayNumber) { + return -(23.0 + 44.0/60.0)*cos(glm::radians((360.0/365.0)*(dayNumber + 10.0))); +} + +void SunSkyStage::updateGraphicsObject() const { + // Always update the sunLongitude based on the current dayTime and the current origin + // The day time is supposed to be local at the origin + float signedNormalizedDayTime = (_dayTime - NUM_HOURS_PER_HALF_DAY) / NUM_HOURS_PER_HALF_DAY; + float sunLongitude = _earthSunModel.getLongitude() + (MAX_LONGITUDE * signedNormalizedDayTime); + _earthSunModel.setSunLongitude(sunLongitude); + + // And update the sunLAtitude as the declinaison depending of the time of the year + _earthSunModel.setSunLatitude(evalSunDeclinaison(_yearTime)); + + if (isSunModelEnabled()) { + Vec3d sunLightDir = -_earthSunModel.getSurfaceSunDir(); + _sunLight->setDirection(Vec3(sunLightDir.x, sunLightDir.y, sunLightDir.z)); + + double originAlt = _earthSunModel.getAltitude(); + _sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f)); + } + + // Background + switch (getBackgroundMode()) { + case NO_BACKGROUND: { + break; + } + case SKY_DOME: { + break; + } + case SKY_BOX: { + break; + } + case NUM_BACKGROUND_MODES: + Q_UNREACHABLE(); + }; +} + +void SunSkyStage::setBackgroundMode(BackgroundMode mode) { + _backgroundMode = mode; + invalidate(); +} + +void SunSkyStage::setSkybox(const SkyboxPointer& skybox) { + _skybox = skybox; + invalidate(); +} diff --git a/libraries/model/src/model/Stage.h b/libraries/model/src/model/Stage.h index 89a8a4c049..978c308ac6 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/model/src/model/Stage.h @@ -1,247 +1,247 @@ -// -// Stage.h -// libraries/model/src/model -// -// Created by Sam Gateau on 2/24/2015. -// 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_model_Stage_h -#define hifi_model_Stage_h - -#include "gpu/Pipeline.h" - -#include "Light.h" -#include "Skybox.h" - -namespace model { - -typedef glm::dvec3 Vec3d; -typedef glm::dvec4 Vec4d; -typedef glm::dmat4 Mat4d; -typedef glm::mat4 Mat4; - -class EarthSunModel { -public: - - void setScale(float scale); - float getScale() const { return _scale; } - - void setLatitude(float lat); - float getLatitude() const { return _latitude; } - void setLongitude(float lon); - float getLongitude() const { return _longitude; } - void setAltitude(float altitude); - float getAltitude() const { return _altitude; } - - - void setSurfaceOrientation(const Quat& orientation); - const Quat& getSurfaceOrientation() const { valid(); return _surfaceOrientation; } - - const Vec3d& getSurfacePos() const { valid(); return _surfacePos; } - - const Mat4d& getSurfaceToWorldMat() const { valid(); return _surfaceToWorldMat; } - const Mat4d& getWoldToSurfaceMat() const { valid(); return _worldToSurfaceMat; } - - const Mat4d& getEyeToSurfaceMat() const { valid(); return _eyeToSurfaceMat; } - const Mat4d& getSurfaceToEyeMat() const { valid(); return _surfaceToEyeMat; } - - const Mat4d& getEyeToWorldMat() const { valid(); return _eyeToWorldMat; } - const Mat4d& getWorldToEyeMat() const { valid(); return _worldToEyeMat; } - - - //or set the surfaceToEye mat directly - void setEyeToSurfaceMat( const Mat4d& e2s); - - const Vec3d& getEyePos() const { valid(); return _eyePos; } - const Vec3d& getEyeDir() const { valid(); return _eyeDir; } - - void setSunLongitude(float lon); - float getSunLongitude() const { return _sunLongitude; } - - void setSunLatitude(float lat); - float getSunLatitude() const { return _sunLatitude; } - - const Vec3d& getWorldSunDir() const { valid(); return _sunDir; } - const Vec3d& getSurfaceSunDir() const { valid(); return _surfaceSunDir; } - - - EarthSunModel() { valid(); } - -protected: - float _scale = 1000.0f; //Km - float _earthRadius = 6360.0; - - Quat _surfaceOrientation; - - float _longitude = 0.0f; - float _latitude = 0.0f; - float _altitude = 0.01f; - mutable Vec3d _surfacePos; - mutable Mat4d _worldToSurfaceMat; - mutable Mat4d _surfaceToWorldMat; - void updateWorldToSurface() const; - - mutable Mat4d _surfaceToEyeMat; - mutable Mat4d _eyeToSurfaceMat; - mutable Vec3d _eyeDir; - mutable Vec3d _eyePos; - void updateSurfaceToEye() const; - - mutable Mat4d _worldToEyeMat; - mutable Mat4d _eyeToWorldMat; - - float _sunLongitude = 0.0f; - float _sunLatitude = 0.0f; - mutable Vec3d _sunDir; - mutable Vec3d _surfaceSunDir; - void updateSun() const; - - mutable bool _invalid = true; - void invalidate() const { _invalid = true; } - void valid() const { if (_invalid) { updateAll(); _invalid = false; } } - void updateAll() const; - - static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale); -}; - - -class Atmosphere { -public: - - Atmosphere(); - Atmosphere(const Atmosphere& atmosphere); - Atmosphere& operator= (const Atmosphere& atmosphere); - virtual ~Atmosphere() {}; - - - void setScatteringWavelength(Vec3 wavelength); - const Vec3& getScatteringWavelength() const { return _scatteringWavelength; } - - void setRayleighScattering(float scattering); - float getRayleighScattering() const { return _rayleighScattering; } - - void setMieScattering(float scattering); - float getMieScattering() const { return _mieScattering; } - - void setSunBrightness(float brightness); - float getSunBrightness() const { return _sunBrightness; } - - void setInnerOuterRadiuses(float inner, float outer); - float getInnerRadius() const { return getData()._radiuses.x; } - float getOuterRadius() const { return getData()._radiuses.y; } - - // Data to access the attribute values of the atmosphere - class Data { - public: - Vec4 _invWaveLength = Vec4(0.0f); - Vec4 _radiuses = Vec4(6000.0f, 6025.0f, 0.0f, 0.0f); - Vec4 _scales = Vec4(0.0f, 0.25f, 0.0f, 0.0f); - Vec4 _scatterings = Vec4(0.0f); - Vec4 _control = Vec4(2.0f, -0.990f, -0.990f*-0.990f, 0.f); - - Data() {} - }; - - const UniformBufferView& getDataBuffer() const { return _dataBuffer; } - -protected: - UniformBufferView _dataBuffer; - Vec3 _scatteringWavelength = Vec3(0.650f, 0.570f, 0.475f); - float _rayleighScattering = 0.0025f; - float _mieScattering = 0.0010f; - float _sunBrightness = 20.0f; - - const Data& getData() const { return _dataBuffer.get(); } - Data& editData() { return _dataBuffer.edit(); } - - void updateScattering(); -}; -typedef std::shared_ptr< Atmosphere > AtmospherePointer; - -// Sun sky stage generates the rendering primitives to display a scene realistically -// at the specified location and time around earth -class SunSkyStage { -public: - - SunSkyStage(); - ~SunSkyStage(); - - // time of the day (local to the position) expressed in decimal hour in the range [0.0, 24.0] - void setDayTime(float hour); - float getDayTime() const { return _dayTime; } - - // time of the year expressed in day in the range [0, 365] - void setYearTime(unsigned int day); - unsigned int getYearTime() const { return _yearTime; } - - // Origin orientation used to modify the cardinal axis alignement used. - // THe default is north along +Z axis and west along +X axis. this orientation gets added - // to the transform stack producing the sun light direction. - void setOriginOrientation(const Quat& orientation); - const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } - - // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] - void setOriginLocation(float longitude, float latitude, float surfaceAltitude); - float getOriginLatitude() const { return _earthSunModel.getLatitude(); } - float getOriginLongitude() const { return _earthSunModel.getLongitude(); } - float getOriginSurfaceAltitude() const { return _earthSunModel.getAltitude(); } - - // Enable / disable the effect of the time and location on the sun direction and color - void setSunModelEnable(bool isEnabled); - bool isSunModelEnabled() const { return _sunModelEnable; } - - // Sun properties - void setSunColor(const Vec3& color); - const Vec3& getSunColor() const { return getSunLight()->getColor(); } - void setSunIntensity(float intensity); - float getSunIntensity() const { return getSunLight()->getIntensity(); } - void setSunAmbientIntensity(float intensity); - float getSunAmbientIntensity() const { return getSunLight()->getAmbientIntensity(); } - - // The sun direction is expressed in the world space - void setSunDirection(const Vec3& direction); - const Vec3& getSunDirection() const { return getSunLight()->getDirection(); } - - LightPointer getSunLight() const { valid(); return _sunLight; } - AtmospherePointer getAtmosphere() const { valid(); return _atmosphere; } - - enum BackgroundMode { - NO_BACKGROUND = 0, - SKY_DOME, - SKY_BOX, - - NUM_BACKGROUND_MODES, - }; - void setBackgroundMode(BackgroundMode mode); - BackgroundMode getBackgroundMode() const { return _backgroundMode; } - - // Skybox - void setSkybox(const SkyboxPointer& skybox); - const SkyboxPointer& getSkybox() const { valid(); return _skybox; } - -protected: - BackgroundMode _backgroundMode = SKY_BOX; - - LightPointer _sunLight; - AtmospherePointer _atmosphere; - mutable SkyboxPointer _skybox; - - float _dayTime = 12.0f; - int _yearTime = 0; - mutable EarthSunModel _earthSunModel; - bool _sunModelEnable = true; - - mutable bool _invalid = true; - void invalidate() const { _invalid = true; } - void valid() const { if (_invalid) { updateGraphicsObject(); _invalid = false; } } - void updateGraphicsObject() const; -}; - -typedef std::shared_ptr< SunSkyStage > SunSkyStagePointer; - -}; - -#endif +// +// Stage.h +// libraries/model/src/model +// +// Created by Sam Gateau on 2/24/2015. +// 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_model_Stage_h +#define hifi_model_Stage_h + +#include "gpu/Pipeline.h" + +#include "Light.h" +#include "Skybox.h" + +namespace model { + +typedef glm::dvec3 Vec3d; +typedef glm::dvec4 Vec4d; +typedef glm::dmat4 Mat4d; +typedef glm::mat4 Mat4; + +class EarthSunModel { +public: + + void setScale(float scale); + float getScale() const { return _scale; } + + void setLatitude(float lat); + float getLatitude() const { return _latitude; } + void setLongitude(float lon); + float getLongitude() const { return _longitude; } + void setAltitude(float altitude); + float getAltitude() const { return _altitude; } + + + void setSurfaceOrientation(const Quat& orientation); + const Quat& getSurfaceOrientation() const { valid(); return _surfaceOrientation; } + + const Vec3d& getSurfacePos() const { valid(); return _surfacePos; } + + const Mat4d& getSurfaceToWorldMat() const { valid(); return _surfaceToWorldMat; } + const Mat4d& getWoldToSurfaceMat() const { valid(); return _worldToSurfaceMat; } + + const Mat4d& getEyeToSurfaceMat() const { valid(); return _eyeToSurfaceMat; } + const Mat4d& getSurfaceToEyeMat() const { valid(); return _surfaceToEyeMat; } + + const Mat4d& getEyeToWorldMat() const { valid(); return _eyeToWorldMat; } + const Mat4d& getWorldToEyeMat() const { valid(); return _worldToEyeMat; } + + + //or set the surfaceToEye mat directly + void setEyeToSurfaceMat( const Mat4d& e2s); + + const Vec3d& getEyePos() const { valid(); return _eyePos; } + const Vec3d& getEyeDir() const { valid(); return _eyeDir; } + + void setSunLongitude(float lon); + float getSunLongitude() const { return _sunLongitude; } + + void setSunLatitude(float lat); + float getSunLatitude() const { return _sunLatitude; } + + const Vec3d& getWorldSunDir() const { valid(); return _sunDir; } + const Vec3d& getSurfaceSunDir() const { valid(); return _surfaceSunDir; } + + + EarthSunModel() { valid(); } + +protected: + float _scale = 1000.0f; //Km + float _earthRadius = 6360.0; + + Quat _surfaceOrientation; + + float _longitude = 0.0f; + float _latitude = 0.0f; + float _altitude = 0.01f; + mutable Vec3d _surfacePos; + mutable Mat4d _worldToSurfaceMat; + mutable Mat4d _surfaceToWorldMat; + void updateWorldToSurface() const; + + mutable Mat4d _surfaceToEyeMat; + mutable Mat4d _eyeToSurfaceMat; + mutable Vec3d _eyeDir; + mutable Vec3d _eyePos; + void updateSurfaceToEye() const; + + mutable Mat4d _worldToEyeMat; + mutable Mat4d _eyeToWorldMat; + + float _sunLongitude = 0.0f; + float _sunLatitude = 0.0f; + mutable Vec3d _sunDir; + mutable Vec3d _surfaceSunDir; + void updateSun() const; + + mutable bool _invalid = true; + void invalidate() const { _invalid = true; } + void valid() const { if (_invalid) { updateAll(); _invalid = false; } } + void updateAll() const; + + static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale); +}; + + +class Atmosphere { +public: + + Atmosphere(); + Atmosphere(const Atmosphere& atmosphere); + Atmosphere& operator= (const Atmosphere& atmosphere); + virtual ~Atmosphere() {}; + + + void setScatteringWavelength(Vec3 wavelength); + const Vec3& getScatteringWavelength() const { return _scatteringWavelength; } + + void setRayleighScattering(float scattering); + float getRayleighScattering() const { return _rayleighScattering; } + + void setMieScattering(float scattering); + float getMieScattering() const { return _mieScattering; } + + void setSunBrightness(float brightness); + float getSunBrightness() const { return _sunBrightness; } + + void setInnerOuterRadiuses(float inner, float outer); + float getInnerRadius() const { return getData()._radiuses.x; } + float getOuterRadius() const { return getData()._radiuses.y; } + + // Data to access the attribute values of the atmosphere + class Data { + public: + Vec4 _invWaveLength = Vec4(0.0f); + Vec4 _radiuses = Vec4(6000.0f, 6025.0f, 0.0f, 0.0f); + Vec4 _scales = Vec4(0.0f, 0.25f, 0.0f, 0.0f); + Vec4 _scatterings = Vec4(0.0f); + Vec4 _control = Vec4(2.0f, -0.990f, -0.990f*-0.990f, 0.f); + + Data() {} + }; + + const UniformBufferView& getDataBuffer() const { return _dataBuffer; } + +protected: + UniformBufferView _dataBuffer; + Vec3 _scatteringWavelength = Vec3(0.650f, 0.570f, 0.475f); + float _rayleighScattering = 0.0025f; + float _mieScattering = 0.0010f; + float _sunBrightness = 20.0f; + + const Data& getData() const { return _dataBuffer.get(); } + Data& editData() { return _dataBuffer.edit(); } + + void updateScattering(); +}; +typedef std::shared_ptr< Atmosphere > AtmospherePointer; + +// Sun sky stage generates the rendering primitives to display a scene realistically +// at the specified location and time around earth +class SunSkyStage { +public: + + SunSkyStage(); + ~SunSkyStage(); + + // time of the day (local to the position) expressed in decimal hour in the range [0.0, 24.0] + void setDayTime(float hour); + float getDayTime() const { return _dayTime; } + + // time of the year expressed in day in the range [0, 365] + void setYearTime(unsigned int day); + unsigned int getYearTime() const { return _yearTime; } + + // Origin orientation used to modify the cardinal axis alignement used. + // THe default is north along +Z axis and west along +X axis. this orientation gets added + // to the transform stack producing the sun light direction. + void setOriginOrientation(const Quat& orientation); + const Quat& getOriginOrientation() const { return _earthSunModel.getSurfaceOrientation(); } + + // Location used to define the sun & sky is a longitude and latitude [rad] and a earth surface altitude [km] + void setOriginLocation(float longitude, float latitude, float surfaceAltitude); + float getOriginLatitude() const { return _earthSunModel.getLatitude(); } + float getOriginLongitude() const { return _earthSunModel.getLongitude(); } + float getOriginSurfaceAltitude() const { return _earthSunModel.getAltitude(); } + + // Enable / disable the effect of the time and location on the sun direction and color + void setSunModelEnable(bool isEnabled); + bool isSunModelEnabled() const { return _sunModelEnable; } + + // Sun properties + void setSunColor(const Vec3& color); + const Vec3& getSunColor() const { return getSunLight()->getColor(); } + void setSunIntensity(float intensity); + float getSunIntensity() const { return getSunLight()->getIntensity(); } + void setSunAmbientIntensity(float intensity); + float getSunAmbientIntensity() const { return getSunLight()->getAmbientIntensity(); } + + // The sun direction is expressed in the world space + void setSunDirection(const Vec3& direction); + const Vec3& getSunDirection() const { return getSunLight()->getDirection(); } + + LightPointer getSunLight() const { valid(); return _sunLight; } + AtmospherePointer getAtmosphere() const { valid(); return _atmosphere; } + + enum BackgroundMode { + NO_BACKGROUND = 0, + SKY_DOME, + SKY_BOX, + + NUM_BACKGROUND_MODES, + }; + void setBackgroundMode(BackgroundMode mode); + BackgroundMode getBackgroundMode() const { return _backgroundMode; } + + // Skybox + void setSkybox(const SkyboxPointer& skybox); + const SkyboxPointer& getSkybox() const { valid(); return _skybox; } + +protected: + BackgroundMode _backgroundMode = SKY_BOX; + + LightPointer _sunLight; + AtmospherePointer _atmosphere; + mutable SkyboxPointer _skybox; + + float _dayTime = 12.0f; + int _yearTime = 0; + mutable EarthSunModel _earthSunModel; + bool _sunModelEnable = true; + + mutable bool _invalid = true; + void invalidate() const { _invalid = true; } + void valid() const { if (_invalid) { updateGraphicsObject(); _invalid = false; } } + void updateGraphicsObject() const; +}; + +typedef std::shared_ptr< SunSkyStage > SunSkyStagePointer; + +}; + +#endif diff --git a/libraries/octree/src/Plane.cpp b/libraries/octree/src/Plane.cpp index e9488e7e57..8e782dd087 100644 --- a/libraries/octree/src/Plane.cpp +++ b/libraries/octree/src/Plane.cpp @@ -1,71 +1,71 @@ -// -// Plane.cpp -// libraries/octree/src/ -// -// Created by Brad Hefta-Gaub on 04/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards -// Simple plane class. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "Plane.h" -#include "OctreeLogging.h" - - -#include - -#include - -void Plane::set3Points(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { - glm::vec3 linev1v2, linev1v3; - - linev1v2 = v2 - v1; - linev1v3 = v3 - v1; - - // this will be perpendicular to both lines - _normal = glm::cross(linev1v2,linev1v3); - _normal = glm::normalize(_normal); - - // this is a point on the plane - _point = v2; - - // the D coefficient from the form Ax+By+Cz=D - _dCoefficient = -(glm::dot(_normal,_point)); -} - -void Plane::setNormalAndPoint(const glm::vec3 &normal, const glm::vec3 &point) { - _point = point; - _normal = normal; - glm::normalize(_normal); - - // the D coefficient from the form Ax+By+Cz=D - _dCoefficient = -(glm::dot(_normal,_point)); -} - -void Plane::setCoefficients(float a, float b, float c, float d) { - // set the normal vector - _normal = glm::vec3(a,b,c); - - //compute the lenght of the vector - float l = glm::length(_normal); - - // normalize the vector - _normal = glm::vec3(a/l,b/l,c/l); - - // and divide d by th length as well - _dCoefficient = d/l; -} - -float Plane::distance(const glm::vec3 &point) const { - return (_dCoefficient + glm::dot(_normal,point)); -} - -void Plane::print() const { - qCDebug(octree, "Plane - point (x=%f y=%f z=%f) normal (x=%f y=%f z=%f) d=%f", - (double)_point.x, (double)_point.y, (double)_point.z, - (double)_normal.x, (double)_normal.y, (double)_normal.z, (double)_dCoefficient); -} +// +// Plane.cpp +// libraries/octree/src/ +// +// Created by Brad Hefta-Gaub on 04/11/13. +// Copyright 2013 High Fidelity, Inc. +// +// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards +// Simple plane class. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Plane.h" +#include "OctreeLogging.h" + + +#include + +#include + +void Plane::set3Points(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { + glm::vec3 linev1v2, linev1v3; + + linev1v2 = v2 - v1; + linev1v3 = v3 - v1; + + // this will be perpendicular to both lines + _normal = glm::cross(linev1v2,linev1v3); + _normal = glm::normalize(_normal); + + // this is a point on the plane + _point = v2; + + // the D coefficient from the form Ax+By+Cz=D + _dCoefficient = -(glm::dot(_normal,_point)); +} + +void Plane::setNormalAndPoint(const glm::vec3 &normal, const glm::vec3 &point) { + _point = point; + _normal = normal; + glm::normalize(_normal); + + // the D coefficient from the form Ax+By+Cz=D + _dCoefficient = -(glm::dot(_normal,_point)); +} + +void Plane::setCoefficients(float a, float b, float c, float d) { + // set the normal vector + _normal = glm::vec3(a,b,c); + + //compute the lenght of the vector + float l = glm::length(_normal); + + // normalize the vector + _normal = glm::vec3(a/l,b/l,c/l); + + // and divide d by th length as well + _dCoefficient = d/l; +} + +float Plane::distance(const glm::vec3 &point) const { + return (_dCoefficient + glm::dot(_normal,point)); +} + +void Plane::print() const { + qCDebug(octree, "Plane - point (x=%f y=%f z=%f) normal (x=%f y=%f z=%f) d=%f", + (double)_point.x, (double)_point.y, (double)_point.z, + (double)_normal.x, (double)_normal.y, (double)_normal.z, (double)_dCoefficient); +} diff --git a/libraries/octree/src/Plane.h b/libraries/octree/src/Plane.h index a113c753cc..db809ad542 100644 --- a/libraries/octree/src/Plane.h +++ b/libraries/octree/src/Plane.h @@ -1,47 +1,47 @@ -// -// Plane.h -// libraries/octree/src/ -// -// Created by Brad Hefta-Gaub on 04/11/13. -// Copyright 2013 High Fidelity, Inc. -// -// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards -// Simple plane class. -// -// 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_Plane_h -#define hifi_Plane_h - -#include - -class Plane { -public: - Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { set3Points(v1,v2,v3); } - Plane() : _normal(0,0,0), _point(0,0,0), _dCoefficient(0) {}; - ~Plane() {} ; - - // methods for defining the plane - void set3Points(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3); - void setNormalAndPoint(const glm::vec3 &normal, const glm::vec3 &point); - void setCoefficients(float a, float b, float c, float d); - - // getters - const glm::vec3& getNormal() const { return _normal; }; - const glm::vec3& getPoint() const { return _point; }; - float getDCoefficient() const { return _dCoefficient; }; - - // utilities - float distance(const glm::vec3 &point) const; - void print() const; - -private: - glm::vec3 _normal; - glm::vec3 _point; - float _dCoefficient; -}; - - +// +// Plane.h +// libraries/octree/src/ +// +// Created by Brad Hefta-Gaub on 04/11/13. +// Copyright 2013 High Fidelity, Inc. +// +// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards +// Simple plane class. +// +// 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_Plane_h +#define hifi_Plane_h + +#include + +class Plane { +public: + Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { set3Points(v1,v2,v3); } + Plane() : _normal(0,0,0), _point(0,0,0), _dCoefficient(0) {}; + ~Plane() {} ; + + // methods for defining the plane + void set3Points(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3); + void setNormalAndPoint(const glm::vec3 &normal, const glm::vec3 &point); + void setCoefficients(float a, float b, float c, float d); + + // getters + const glm::vec3& getNormal() const { return _normal; }; + const glm::vec3& getPoint() const { return _point; }; + float getDCoefficient() const { return _dCoefficient; }; + + // utilities + float distance(const glm::vec3 &point) const; + void print() const; + +private: + glm::vec3 _normal; + glm::vec3 _point; + float _dCoefficient; +}; + + #endif // hifi_Plane_h \ No newline at end of file diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 022bf7898a..bd61de7338 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -16,66 +16,66 @@ #include #include "ProceduralSkybox_vert.h" -#include "ProceduralSkybox_frag.h" - -ProceduralSkybox::ProceduralSkybox() : model::Skybox() { -} - -ProceduralSkybox::ProceduralSkybox(const ProceduralSkybox& skybox) : - model::Skybox(skybox), - _procedural(skybox._procedural) { - -} - -void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) { - _procedural = procedural; - if (_procedural) { - _procedural->_vertexSource = ProceduralSkybox_vert; - _procedural->_fragmentSource = ProceduralSkybox_frag; - // Adjust the pipeline state for background using the stencil test - _procedural->_state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); - } -} - -void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const { - ProceduralSkybox::render(batch, frustum, (*this)); -} - -void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) { - if (!(skybox._procedural)) { - skybox.updateDataBuffer(); - Skybox::render(batch, viewFrustum, skybox); - } - - static gpu::BufferPointer theBuffer; - static gpu::Stream::FormatPointer theFormat; - - if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { - if (!theBuffer) { - const float CLIP = 1.0f; - const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; - theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); - theFormat = std::make_shared(); - theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); - } - - glm::mat4 projMat; - viewFrustum.evalProjectionMatrix(projMat); - - Transform viewTransform; - viewFrustum.evalViewTransform(viewTransform); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewTransform); - batch.setModelTransform(Transform()); // only for Mac - batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); - batch.setInputFormat(theFormat); - - if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { - batch.setResourceTexture(0, skybox.getCubemap()); - } - - skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1)); - batch.draw(gpu::TRIANGLE_STRIP, 4); - } -} - +#include "ProceduralSkybox_frag.h" + +ProceduralSkybox::ProceduralSkybox() : model::Skybox() { +} + +ProceduralSkybox::ProceduralSkybox(const ProceduralSkybox& skybox) : + model::Skybox(skybox), + _procedural(skybox._procedural) { + +} + +void ProceduralSkybox::setProcedural(const ProceduralPointer& procedural) { + _procedural = procedural; + if (_procedural) { + _procedural->_vertexSource = ProceduralSkybox_vert; + _procedural->_fragmentSource = ProceduralSkybox_frag; + // Adjust the pipeline state for background using the stencil test + _procedural->_state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + } +} + +void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& frustum) const { + ProceduralSkybox::render(batch, frustum, (*this)); +} + +void ProceduralSkybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const ProceduralSkybox& skybox) { + if (!(skybox._procedural)) { + skybox.updateDataBuffer(); + Skybox::render(batch, viewFrustum, skybox); + } + + static gpu::BufferPointer theBuffer; + static gpu::Stream::FormatPointer theFormat; + + if (skybox._procedural && skybox._procedural->_enabled && skybox._procedural->ready()) { + if (!theBuffer) { + const float CLIP = 1.0f; + const glm::vec2 vertices[4] = { { -CLIP, -CLIP }, { CLIP, -CLIP }, { -CLIP, CLIP }, { CLIP, CLIP } }; + theBuffer = std::make_shared(sizeof(vertices), (const gpu::Byte*) vertices); + theFormat = std::make_shared(); + theFormat->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::XYZ)); + } + + glm::mat4 projMat; + viewFrustum.evalProjectionMatrix(projMat); + + Transform viewTransform; + viewFrustum.evalViewTransform(viewTransform); + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewTransform); + batch.setModelTransform(Transform()); // only for Mac + batch.setInputBuffer(gpu::Stream::POSITION, theBuffer, 0, 8); + batch.setInputFormat(theFormat); + + if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) { + batch.setResourceTexture(0, skybox.getCubemap()); + } + + skybox._procedural->prepare(batch, glm::vec3(0), glm::vec3(1)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + } +} + diff --git a/libraries/render-utils/src/DeferredBuffer.slh b/libraries/render-utils/src/DeferredBuffer.slh index 89b8b26a4f..275966534a 100755 --- a/libraries/render-utils/src/DeferredBuffer.slh +++ b/libraries/render-utils/src/DeferredBuffer.slh @@ -25,19 +25,19 @@ uniform sampler2D specularMap; uniform sampler2D depthMap; -struct DeferredTransform { +struct DeferredTransform { mat4 projection; mat4 viewInverse; - vec4 stereoSide_spareABC; -}; - -layout(std140) uniform deferredTransformBuffer { - DeferredTransform _deferredTransform; -}; -DeferredTransform getDeferredTransform() { - return _deferredTransform; -} + vec4 stereoSide_spareABC; +}; + +layout(std140) uniform deferredTransformBuffer { + DeferredTransform _deferredTransform; +}; +DeferredTransform getDeferredTransform() { + return _deferredTransform; +} bool getStereoMode(DeferredTransform deferredTransform) { return (deferredTransform.stereoSide_spareABC.x != 0.0); From ef6c4f6f665c56bd37ae31dede4241a424a95762 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 19 Oct 2015 19:31:25 -0700 Subject: [PATCH 0265/1003] 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 0266/1003] 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 11f2d29bf80ce439ac3c697d7cdaffceea03f7f5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 20 Oct 2015 10:36:37 -0700 Subject: [PATCH 0267/1003] AnimBlendLinear: bugfixes for sync flag added timeScale --- .../defaultAvatar_full/avatar-animation.json | 37 +++++++++-- libraries/animation/src/AnimBlendLinear.cpp | 61 +++++++++++-------- libraries/animation/src/AnimBlendLinear.h | 17 +++--- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimClip.h | 3 + libraries/animation/src/AnimNodeLoader.cpp | 8 ++- 6 files changed, 87 insertions(+), 41 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index be1abbc820..ea4ef63d16 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -191,6 +191,7 @@ "data": { "alpha": 0.0, "sync": false, + "timeScale": 1.0, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -341,6 +342,7 @@ "data": { "alpha": 0.0, "sync": false, + "timeScale": 1.0, "alphaVar": "leftHandGrabBlend" }, "children": [ @@ -525,16 +527,39 @@ }, { "id": "walkFwd", - "type": "clip", + "type": "blendLinear", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", - "startFrame": 0.0, - "endFrame": 35.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true, "timeScaleVar": "walkTimeScale" }, - "children": [] + "children": [ + { + "id": "walkFwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx", + "startFrame": 0.0, + "endFrame": 39.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkFwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", + "startFrame": 0.0, + "endFrame": 35.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "walkBwd", diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 1e65ba2b36..1f9026de9d 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,10 +14,11 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale) : AnimNode(AnimNode::Type::BlendLinear, id), _alpha(alpha), - _sync(sync) { + _sync(sync), + _timeScale(timeScale) { } @@ -28,6 +29,7 @@ AnimBlendLinear::~AnimBlendLinear() { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); + _timeScale = animVars.lookup(_timeScaleVar, _timeScale); if (_children.size() == 0) { for (auto&& pose : _poses) { @@ -42,13 +44,11 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - float prevPoseDeltaTime = dt; - float nextPoseDeltaTime = dt; if (_sync) { - setSyncFrameAndComputeDeltaTime(dt, prevPoseIndex, nextPoseIndex, &prevPoseDeltaTime, &nextPoseDeltaTime, triggersOut); + setSyncAndAccumulateTime(dt, prevPoseIndex, nextPoseIndex, triggersOut); } - evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevPoseDeltaTime, nextPoseDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; } @@ -59,15 +59,14 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { } void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, - size_t prevPoseIndex, size_t nextPoseIndex, - float prevPoseDeltaTime, float nextPoseDeltaTime) { + size_t prevPoseIndex, size_t nextPoseIndex, float dt) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevPoseDeltaTime, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextPoseDeltaTime, triggersOut); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); @@ -77,9 +76,7 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, T } } -void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, - float* prevPoseDeltaTime, float* nextPoseDeltaTime, - Triggers& triggersOut) { +void AnimBlendLinear::setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut) { std::vector offsets(_children.size(), 0.0f); std::vector timeScales(_children.size(), 1.0f); @@ -88,33 +85,45 @@ void AnimBlendLinear::setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseI // abort if we find a child that is NOT a clipNode. if (_children[i]->getType() != AnimNode::Type::Clip) { // TODO: FIXME: make sync this work for other node types. - *prevPoseDeltaTime = dt; - *nextPoseDeltaTime = dt; return; } auto clipNode = std::dynamic_pointer_cast(_children[i]); assert(clipNode); if (clipNode) { - lengthSum += clipNode->getEndFrame() - clipNode->getStartFrame(); + lengthSum += (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; } } - float averageLength = lengthSum / (float)_children.size(); + _averageLength = lengthSum / (float)_children.size(); + + float progress = (_syncFrame / _averageLength); auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); - float prevTimeScale = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) / averageLength; + float prevLength = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float prevOffset = prevClipNode->getStartFrame(); - prevClipNode->setCurrentFrame(prevOffset + prevTimeScale / _syncFrame); + float prevFrame = prevOffset + (progress * prevLength); + float prevTimeScale = _timeScale * (_averageLength / prevLength); + prevClipNode->setTimeScale(prevTimeScale); + prevClipNode->setCurrentFrame(prevFrame); auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); - float nextTimeScale = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) / averageLength; + float nextLength = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; float nextOffset = nextClipNode->getStartFrame(); - nextClipNode->setCurrentFrame(nextOffset + nextTimeScale / _syncFrame); + float nextFrame = nextOffset + (progress * nextLength); + float nextTimeScale = _timeScale * (_averageLength / nextLength); + nextClipNode->setTimeScale(nextTimeScale); + nextClipNode->setCurrentFrame(nextFrame); + const float START_FRAME = 0.0f; const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(0.0f, averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); - - *prevPoseDeltaTime = prevTimeScale; - *nextPoseDeltaTime = nextTimeScale; + _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); } +void AnimBlendLinear::setCurrentFrameInternal(float frame) { + // because dt is 0, we should not encounter any triggers + const float dt = 0.0f; + Triggers triggers; + const float START_FRAME = 0.0f; + const bool LOOP_FLAG = true; + _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, frame, dt, LOOP_FLAG, _id, triggers); +} diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index d8b4f52fb6..7def6be91d 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -30,32 +30,35 @@ class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha, bool sync); + AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, - size_t prevPoseIndex, size_t nextPoseIndex, - float prevPoseDeltaTime, float nextPoseDeltaTime); - void setSyncFrameAndComputeDeltaTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, - float* prevPoseDeltaTime, float* nextPoseDeltaTime, - Triggers& triggersOut); + size_t prevPoseIndex, size_t nextPoseIndex, float dt); + void setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut); + + virtual void setCurrentFrameInternal(float frame) override; AnimPoseVec _poses; float _alpha; bool _sync; + float _timeScale; + float _syncFrame = 0.0f; - float _timeScale = 1.0f; // TODO: HOOK THIS UP TO AN ANIMVAR. + float _averageLength = 0.0f; // average length of child animations in frames. QString _alphaVar; + QString _timeScaleVar; // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 613194164f..8f50874ed3 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -81,7 +81,7 @@ void AnimClip::setCurrentFrameInternal(float frame) { // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame * _timeScale, dt, _loopFlag, _id, triggers); + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggers); } void AnimClip::copyFromNetworkAnim() { diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index b7cd2861fa..36867e622b 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -39,6 +39,9 @@ public: float getStartFrame() const { return _startFrame; } float getEndFrame() const { return _endFrame; } + void setTimeScale(float timeScale) { _timeScale = timeScale; } + float getTimeScale() const { return _timeScale; } + protected: void loadURL(const QString& url); diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 4222364629..f9ebf2a630 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -225,15 +225,21 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); + READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); + READ_OPTIONAL_STRING(timeScaleVar, jsonObj); - auto node = std::make_shared(id, alpha, sync); + auto node = std::make_shared(id, alpha, sync, timeScale); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); } + if (!timeScaleVar.isEmpty()) { + node->setTimeScaleVar(timeScaleVar); + } + return node; } From 22139931f252966396538fb75ef2bd091c89470b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 10:35:53 -0700 Subject: [PATCH 0268/1003] 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 af09871bed6858c3d4e43204b009b2d838d8fe56 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 20 Oct 2015 11:08:28 -0700 Subject: [PATCH 0269/1003] fix basketball solidity in toybox --- examples/toys/basketball/createHoop.js | 8 +++-- examples/toys/basketball/createRack.js | 45 +++++++++++++------------ unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/examples/toys/basketball/createHoop.js b/examples/toys/basketball/createHoop.js index 2beb7c9fca..792232b98f 100644 --- a/examples/toys/basketball/createHoop.js +++ b/examples/toys/basketball/createHoop.js @@ -36,6 +36,10 @@ var hoop = Entities.addEntity({ y: 3.99, z: 3.79 }, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) compoundShapeURL: hoopCollisionHullURL -}); - +}); \ No newline at end of file diff --git a/examples/toys/basketball/createRack.js b/examples/toys/basketball/createRack.js index f764b2ec03..cda1a115d4 100644 --- a/examples/toys/basketball/createRack.js +++ b/examples/toys/basketball/createRack.js @@ -22,8 +22,6 @@ var DIAMETER = 0.30; var RESET_DISTANCE = 1; var MINIMUM_MOVE_LENGTH = 0.05; -var GRABBABLE_DATA_KEY = "grabbableKey"; - var rackStartPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { @@ -53,19 +51,17 @@ var rack = Entities.addEntity({ ignoreForCollisions: false, collisionSoundURL: collisionSoundURL, compoundShapeURL: rackCollisionHullURL, - // scriptURL: rackScriptURL + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) }); - -setEntityCustomData(GRABBABLE_DATA_KEY, rack, { - grabbable: false -}); - -var nonCollidingBalls = []; -var collidingBalls = []; +var balls = []; var originalBallPositions = []; -function createCollidingBalls() { +function createBalls() { var position = rackStartPosition; var i; @@ -76,9 +72,9 @@ function createCollidingBalls() { z: position.z + (DIAMETER) - (DIAMETER * i) }; - var collidingBall = Entities.addEntity({ + var ball = Entities.addEntity({ type: "Model", - name: 'Colliding Basketball', + name: 'Hifi-Basketball', shapeType: 'Sphere', position: ballPosition, dimensions: { @@ -96,16 +92,21 @@ function createCollidingBalls() { collisionsWillMove: true, ignoreForCollisions: false, modelURL: basketballURL, + userData: JSON.stringify({ + grabbableKey: { + invertSolidWhileHeld: true + } + }) }); - collidingBalls.push(collidingBall); + balls.push(ball); originalBallPositions.push(position); } } function testBallDistanceFromStart() { var resetCount = 0; - collidingBalls.forEach(function(ball, index) { + balls.forEach(function(ball, index) { var currentPosition = Entities.getEntityProperties(ball, "position").position; var originalPosition = originalBallPositions[index]; var distance = Vec3.subtract(originalPosition, currentPosition); @@ -117,8 +118,8 @@ function testBallDistanceFromStart() { if (moving < MINIMUM_MOVE_LENGTH) { resetCount++; if (resetCount === NUMBER_OF_BALLS) { - deleteCollidingBalls(); - createCollidingBalls(); + deleteBalls(); + createBalls(); } } }, 200) @@ -128,19 +129,19 @@ function testBallDistanceFromStart() { function deleteEntity(entityID) { if (entityID === rack) { - deleteCollidingBalls(); + deleteBalls(); Script.clearInterval(distanceCheckInterval); Entities.deletingEntity.disconnect(deleteEntity); } } -function deleteCollidingBalls() { - while (collidingBalls.length > 0) { - Entities.deleteEntity(collidingBalls.pop()); +function deleteBalls() { + while (balls.length > 0) { + Entities.deleteEntity(balls.pop()); } } -createCollidingBalls(); +createBalls(); Entities.deletingEntity.connect(deleteEntity); var distanceCheckInterval = Script.setInterval(testBallDistanceFromStart, 1000); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 42056f6c3b..fde97beeb5 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -321,7 +321,7 @@ resetMe: { resetMe: true }, - grabbable: { + grabbableKey: { invertSolidWhileHeld: true } }) diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 2ecd52f344..7316da57d1 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -294,7 +294,7 @@ MasterReset = function() { resetMe: { resetMe: true }, - grabbable: { + grabbableKey: { invertSolidWhileHeld: true } }) From 771e5a4f9de7ab08bb5308b7b43d9528d5e00b58 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 20 Oct 2015 11:21:09 -0700 Subject: [PATCH 0270/1003] add toybox ac scripts, write headers for them, organize --- examples/toys/AC_scripts/arcade_game_sound.js | 52 +++++++++++++++++++ examples/toys/AC_scripts/cat_purr_sound.js | 41 +++++++++++++++ .../toys/AC_scripts/dogs_barking_sound.js | 52 +++++++++++++++++++ examples/toys/AC_scripts/fireplace_sound.js | 38 ++++++++++++++ .../AC_scripts}/flickeringLight.js | 0 examples/toys/AC_scripts/insects_sound.js | 39 ++++++++++++++ examples/toys/AC_scripts/river_water_sound.js | 37 +++++++++++++ examples/toys/AC_scripts/windmill_sound.js | 38 ++++++++++++++ 8 files changed, 297 insertions(+) create mode 100644 examples/toys/AC_scripts/arcade_game_sound.js create mode 100644 examples/toys/AC_scripts/cat_purr_sound.js create mode 100644 examples/toys/AC_scripts/dogs_barking_sound.js create mode 100644 examples/toys/AC_scripts/fireplace_sound.js rename examples/{acScripts => toys/AC_scripts}/flickeringLight.js (100%) create mode 100644 examples/toys/AC_scripts/insects_sound.js create mode 100644 examples/toys/AC_scripts/river_water_sound.js create mode 100644 examples/toys/AC_scripts/windmill_sound.js diff --git a/examples/toys/AC_scripts/arcade_game_sound.js b/examples/toys/AC_scripts/arcade_game_sound.js new file mode 100644 index 0000000000..0f158aa574 --- /dev/null +++ b/examples/toys/AC_scripts/arcade_game_sound.js @@ -0,0 +1,52 @@ +// Adds arcade game noises to Toybox. +// By Ryan Karpf 10/20/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var SOUND_URL = "http://hifi-public.s3.amazonaws.com/ryan/ARCADE_GAMES_VID.L.L.wav"; +var SOUND_POSITION = { x: 543.77, y: 495.07, z: 502.25 }; + +var MINUTE = 60 * 1000; +var PLAY_SOUND_INTERVAL = 1.5 * MINUTE; + +var audioOptions = { + position: SOUND_POSITION, + volume: .01, + loop: false, +}; + +var sound = SoundCache.getSound(SOUND_URL); +var injector = null; + +function playSound() { + print("Playing sound"); + if (injector) { + // try/catch in case the injector QObject has been deleted already + try { + injector.stop(); + } catch (e) { + } + } + injector = Audio.playSound(sound, audioOptions); +} + +function checkDownloaded() { + if (sound.downloaded) { + print("Sound downloaded."); + Script.clearInterval(checkDownloadedTimer); + Script.setInterval(playSound, PLAY_SOUND_INTERVAL); + playSound(); + } +} + +// Check once a second to see if the audio file has been downloaded +var checkDownloadedTimer = Script.setInterval(checkDownloaded, 1000); + +Script.scriptEnding.connect(function() { + if (injector) { + injector.stop(); + } +}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/cat_purr_sound.js b/examples/toys/AC_scripts/cat_purr_sound.js new file mode 100644 index 0000000000..c02b14ebf8 --- /dev/null +++ b/examples/toys/AC_scripts/cat_purr_sound.js @@ -0,0 +1,41 @@ +// +// ambiance.js +// examples +// +// Created by Clément Brisset on 11/18/14. +// 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 +// + +var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/Cat_Purring_Deep_Low_Snor.wav"; +var position = { x: 551.48, y: 495.60, z: 502.08}; +var audioOptions = { + position: position, + volume: .03, + loop: true +}; + +var sound = SoundCache.getSound(soundURL); +var injector = null; +var count = 300; + +Script.update.connect(function() { + if (count > 0) { + count--; + return; + } + + if (sound.downloaded && injector === null) { + print("Sound downloaded."); + injector = Audio.playSound(sound, audioOptions); + print("Playing: " + injector); + } +}); + +Script.scriptEnding.connect(function() { + if (injector !== null) { + injector.stop(); + } +}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/dogs_barking_sound.js b/examples/toys/AC_scripts/dogs_barking_sound.js new file mode 100644 index 0000000000..c362e1ac76 --- /dev/null +++ b/examples/toys/AC_scripts/dogs_barking_sound.js @@ -0,0 +1,52 @@ +// Adds a dogs barking noise to Toybox. +// By Ryan Karpf 10/20/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var SOUND_URL = "http://hifi-public.s3.amazonaws.com/ryan/dogs_barking_1.L.wav"; +var SOUND_POSITION = { x: 523, y: 495, z: 469 }; + +var MINUTE = 60 * 1000; +var PLAY_SOUND_INTERVAL = 1 * MINUTE; + +var audioOptions = { + position: SOUND_POSITION, + volume: .05, + loop: false, +}; + +var sound = SoundCache.getSound(SOUND_URL); +var injector = null; + +function playSound() { + print("Playing sound"); + if (injector) { + // try/catch in case the injector QObject has been deleted already + try { + injector.stop(); + } catch (e) { + } + } + injector = Audio.playSound(sound, audioOptions); +} + +function checkDownloaded() { + if (sound.downloaded) { + print("Sound downloaded."); + Script.clearInterval(checkDownloadedTimer); + Script.setInterval(playSound, PLAY_SOUND_INTERVAL); + playSound(); + } +} + +// Check once a second to see if the audio file has been downloaded +var checkDownloadedTimer = Script.setInterval(checkDownloaded, 1000); + +Script.scriptEnding.connect(function() { + if (injector) { + injector.stop(); + } +}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/fireplace_sound.js b/examples/toys/AC_scripts/fireplace_sound.js new file mode 100644 index 0000000000..76a7153c4b --- /dev/null +++ b/examples/toys/AC_scripts/fireplace_sound.js @@ -0,0 +1,38 @@ +// Adds a fireplace noise to Toybox. +// By Ryan Karpf 10/20/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/demo/0619_Fireplace__Tree_B.L.wav"; +var position = { x: 551.61, y: 494.88, z: 502.00}; +var audioOptions = { + position: position, + volume: .08, + loop: true +}; + +var sound = SoundCache.getSound(soundURL); +var injector = null; +var count = 300; + +Script.update.connect(function() { + if (count > 0) { + count--; + return; + } + + if (sound.downloaded && injector === null) { + print("Sound downloaded."); + injector = Audio.playSound(sound, audioOptions); + print("Playing: " + injector); + } +}); + +Script.scriptEnding.connect(function() { + if (injector !== null) { + injector.stop(); + } +}); \ No newline at end of file diff --git a/examples/acScripts/flickeringLight.js b/examples/toys/AC_scripts/flickeringLight.js similarity index 100% rename from examples/acScripts/flickeringLight.js rename to examples/toys/AC_scripts/flickeringLight.js diff --git a/examples/toys/AC_scripts/insects_sound.js b/examples/toys/AC_scripts/insects_sound.js new file mode 100644 index 0000000000..1783413901 --- /dev/null +++ b/examples/toys/AC_scripts/insects_sound.js @@ -0,0 +1,39 @@ +// Adds an insect noise to Toybox. +// By Ryan Karpf 10/20/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + + +var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/insects3.wav"; +var position = { x: 560, y: 495, z: 474}; +var audioOptions = { + position: position, + volume: .25, + loop: true +}; + +var sound = SoundCache.getSound(soundURL); +var injector = null; +var count = 300; + +Script.update.connect(function() { + if (count > 0) { + count--; + return; + } + + if (sound.downloaded && injector === null) { + print("Sound downloaded."); + injector = Audio.playSound(sound, audioOptions); + print("Playing: " + injector); + } +}); + +Script.scriptEnding.connect(function() { + if (injector !== null) { + injector.stop(); + } +}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/river_water_sound.js b/examples/toys/AC_scripts/river_water_sound.js new file mode 100644 index 0000000000..30df67b515 --- /dev/null +++ b/examples/toys/AC_scripts/river_water_sound.js @@ -0,0 +1,37 @@ +// Adds a river water sound to Toybox. +// By Ryan Karpf 10/20/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/Water_Lap_River_Edge_Gentle.L.wav"; +var position = { x: 580, y: 493, z: 528}; +var audioOptions = { + position: position, + volume: .4, + loop: true +}; + +var sound = SoundCache.getSound(soundURL); +var injector = null; +var count = 300; + +Script.update.connect(function() { + if (count > 0) { + count--; + return; + } + + if (sound.downloaded && injector === null) { + print("Sound downloaded."); + injector = Audio.playSound(sound, audioOptions); + print("Playing: " + injector); + } +}); + +Script.scriptEnding.connect(function() { + if (injector !== null) { + injector.stop(); + } +}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/windmill_sound.js b/examples/toys/AC_scripts/windmill_sound.js new file mode 100644 index 0000000000..2072dbb9dd --- /dev/null +++ b/examples/toys/AC_scripts/windmill_sound.js @@ -0,0 +1,38 @@ +// Adds a windmill noise to toybox. +// By Ryan Karpf 10/20/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/WINDMILL_Mono.wav"; +var position = { x: 530, y: 516, z: 518}; +var audioOptions = { + position: position, + volume: .08, + loop: true +}; + +var sound = SoundCache.getSound(soundURL); +var injector = null; +var count = 300; + +Script.update.connect(function() { + if (count > 0) { + count--; + return; + } + + if (sound.downloaded && injector === null) { + print("Sound downloaded."); + injector = Audio.playSound(sound, audioOptions); + print("Playing: " + injector); + } +}); + +Script.scriptEnding.connect(function() { + if (injector !== null) { + injector.stop(); + } +}); \ No newline at end of file From b0ae55ee63a7161142b300c130089225683fa7da Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 20 Oct 2015 11:28:04 -0700 Subject: [PATCH 0271/1003] update cat purr header --- examples/toys/AC_scripts/cat_purr_sound.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/toys/AC_scripts/cat_purr_sound.js b/examples/toys/AC_scripts/cat_purr_sound.js index c02b14ebf8..0ac05a1974 100644 --- a/examples/toys/AC_scripts/cat_purr_sound.js +++ b/examples/toys/AC_scripts/cat_purr_sound.js @@ -1,14 +1,11 @@ -// -// ambiance.js -// examples -// -// Created by Clément Brisset on 11/18/14. -// Copyright 2014 High Fidelity, Inc. +// Adds a cat purring noise to Toybox. +// By Ryan Karpf 10/20/2015 // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/Cat_Purring_Deep_Low_Snor.wav"; var position = { x: 551.48, y: 495.60, z: 502.08}; var audioOptions = { From 630bad5433fbbc1989f66840b755303b5681aaef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 12:46:54 -0700 Subject: [PATCH 0272/1003] Removing .gitattributes until we can tackle consistent line endings without impacting the team --- .gitattributes | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index bf357796a4..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,25 +0,0 @@ -*.cpp text -*.c text -*.h text -*.qml text -*.js text -*.json text -*.slv text -*.slf text -*.slh text -*.vert text -*.frag text -*.fst text -*.ini text -*.html text -*.ts text -*.txt text - -# Denote all files that are truly binary and should not be modified. -*.png binary -*.jpg binary -*.wav binary -*.fbx binary -*.dds binary -*.svg binary -*.ttf binary From 502fb9cca5ffb0a14ab08e778559dc464eec496c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Oct 2015 13:12:16 -0700 Subject: [PATCH 0273/1003] adding .gitattributes again --- .gitattributes | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..bf357796a4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,25 @@ +*.cpp text +*.c text +*.h text +*.qml text +*.js text +*.json text +*.slv text +*.slf text +*.slh text +*.vert text +*.frag text +*.fst text +*.ini text +*.html text +*.ts text +*.txt text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.wav binary +*.fbx binary +*.dds binary +*.svg binary +*.ttf binary From 4ff05e8c2a882610d68b9c4697b3a1723150e042 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Oct 2015 13:26:19 -0700 Subject: [PATCH 0274/1003] fix lin endings of some JS files --- .../resources/web/settings/js/form2js.min.js | 52 +- .../web/stats/js/highcharts-custom.js | 2 +- examples/example/misc/sunLightExample.js | 228 +- examples/leaves.js | 662 +++--- examples/libraries/walkApi.js | 1886 ++++++++--------- examples/libraries/walkConstants.js | 108 +- examples/libraries/walkFilters.js | 408 ++-- examples/libraries/walkSettings.js | 194 +- examples/toys/flashlight/createFlashlight.js | 64 +- examples/toys/flashlight/flashlight.js | 538 ++--- examples/utilities/tools/renderEngineDebug.js | 174 +- examples/walk.js | 908 ++++---- 12 files changed, 2612 insertions(+), 2612 deletions(-) diff --git a/domain-server/resources/web/settings/js/form2js.min.js b/domain-server/resources/web/settings/js/form2js.min.js index f1e610f7c3..2279102b25 100644 --- a/domain-server/resources/web/settings/js/form2js.min.js +++ b/domain-server/resources/web/settings/js/form2js.min.js @@ -1,26 +1,26 @@ -/** - * Copyright (c) 2010 Maxim Vasiliev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author Maxim Vasiliev - * Date: 09.09.2010 - * Time: 19:02:33 - */ -(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.form2js=t()}})(this,function(){"use strict";function e(e,r,i,s,o,u){u=u?true:false;if(typeof i=="undefined"||i==null)i=true;if(typeof r=="undefined"||r==null)r=".";if(arguments.length<5)o=false;e=typeof e=="string"?document.getElementById(e):e;var a=[],f,l=0;if(e.constructor==Array||typeof NodeList!="undefined"&&e.constructor==NodeList){while(f=e[l++]){a=a.concat(n(f,s,o,u))}}else{a=n(e,s,o,u)}return t(a,i,r)}function t(e,t,n){var r={},i={},s,o,u,a,f,l,c,h,p,d,v,m,g;for(s=0;s1){for(u=0;u-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i1){for(u=0;u-1&&o==l.length-1){p=v.substr(0,v.indexOf("["));h+=p;if(!c[p])c[p]=[];c[p].push(f)}else if(v.indexOf("[")>-1){p=v.substr(0,v.indexOf("["));d=v.replace(/(^([a-z_]+)?\[)|(\]$)/gi,"");h+="_"+p+"_"+d;if(!i[h])i[h]={};if(p!=""&&!c[p])c[p]=[];if(o==l.length-1){if(p==""){c.push(f);i[h][d]=c[c.length-1]}else{c[p].push(f);i[h][d]=c[p][c[p].length-1]}}else{if(!i[h][d]){if(/^[0-9a-z_]+\[?/i.test(l[o+1]))c[p].push({});else c[p].push([]);i[h][d]=c[p][c[p].length-1]}}c=i[h][d]}else{h+=v;if(o0?o:r(e,t,n,s)}function r(e,t,n,r){var s=[],o=e.firstChild;while(o){s=s.concat(i(o,t,n,r));o=o.nextSibling}return s}function i(e,t,n,i){if(e.disabled&&!i)return[];var u,a,f,l=s(e,n);u=t&&t(e);if(u&&u.name){f=[u]}else if(l!=""&&e.nodeName.match(/INPUT|TEXTAREA/i)){a=o(e,i);if(null===a){f=[]}else{f=[{name:l,value:a}]}}else if(l!=""&&e.nodeName.match(/SELECT/i)){a=o(e,i);f=[{name:l.replace(/\[\]$/,""),value:a}]}else{f=r(e,t,n,i)}return f}function s(e,t){if(e.name&&e.name!="")return e.name;else if(t&&e.id&&e.id!="")return e.id;else return""}function o(e,t){if(e.disabled&&!t)return null;switch(e.nodeName){case"INPUT":case"TEXTAREA":switch(e.type.toLowerCase()){case"radio":if(e.checked&&e.value==="false")return false;case"checkbox":if(e.checked&&e.value==="true")return true;if(!e.checked&&e.value==="true")return false;if(e.checked)return e.value;break;case"button":case"reset":case"submit":case"image":return"";break;default:return e.value;break}break;case"SELECT":return u(e);break;default:break}return null}function u(e){var t=e.multiple,n=[],r,i,s;if(!t)return e.value;for(r=e.getElementsByTagName("option"),i=0,s=r.length;i=f.min&&c<=f.max)for(h=b[k+1],c=d===u?0:d+1,d=b[k+1]?T(x(0,K((e.clientX+(h?h.wrappedClientX||h.clientX:g))/2)),g):g;0<=c&&c<=d;)l[c++]=e;this.tooltipPoints=l}},show:function(){this.setVisible(!0)}, hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===u?!this.selected:a;this.checkbox&&(this.checkbox.checked=a);B(this,a?"select":"unselect")},drawTracker:$a.drawTrackerGraph});v(U,{Axis:ma,Chart:Ea,Color:za,Point:Ca,Tick:xa,Renderer:Ja,Series:fa,SVGElement:O,SVGRenderer:Ja,arrayMin:La,arrayMax:va,charts:Y,dateFormat:Ka,format:ua,pathAnim:ib,getOptions:function(){return M},hasBidiBug:Db,isTouchDevice:wb,numberFormat:ka,seriesTypes:P,setOptions:function(a){M=G(!0,M,a);pb(); -return M},addEvent:F,removeEvent:L,createElement:ja,discardElement:Na,css:J,each:t,extend:v,map:kb,merge:G,pick:n,splat:ea,extendClass:nb,pInt:E,wrap:bb,svg:Z,canvas:ca,vml:!Z&&!ca,product:"Highcharts 4.0.4",version:"/Highstock 2.0.4"})})(); +return M},addEvent:F,removeEvent:L,createElement:ja,discardElement:Na,css:J,each:t,extend:v,map:kb,merge:G,pick:n,splat:ea,extendClass:nb,pInt:E,wrap:bb,svg:Z,canvas:ca,vml:!Z&&!ca,product:"Highcharts 4.0.4",version:"/Highstock 2.0.4"})})(); diff --git a/examples/example/misc/sunLightExample.js b/examples/example/misc/sunLightExample.js index 45cf353620..772ca68cec 100644 --- a/examples/example/misc/sunLightExample.js +++ b/examples/example/misc/sunLightExample.js @@ -1,114 +1,114 @@ -// -// SunLightExample.js -// examples -// Sam Gateau -// 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("../../utilities/tools/cookies.js"); - -var panel = new Panel(10, 400); - -panel.newSlider("Origin Longitude", -180, 180, - function(value) { Scene.setStageLocation(value, Scene.getStageLocationLatitude(), Scene.getStageLocationAltitude()); }, - function() { return Scene.getStageLocationLongitude(); }, - function(value) { return value.toFixed(0) + " deg"; } -); - -panel.newSlider("Origin Latitude", -90, 90, - function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), value, Scene.getStageLocationAltitude()); }, - function() { return Scene.getStageLocationLatitude(); }, - function(value) { return value.toFixed(0) + " deg"; } -); - -panel.newSlider("Origin Altitude", 0, 1000, - function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), Scene.getStageLocationLatitude(), value); }, - function() { return Scene.getStageLocationAltitude(); }, - function(value) { return (value).toFixed(0) + " km"; } -); - -panel.newSlider("Year Time", 0, 364, - function(value) { Scene.setStageYearTime(value); }, - function() { return Scene.getStageYearTime(); }, - function(value) { - var numDaysPerMonth = 365.0 / 12.0; - var monthly = (value / numDaysPerMonth); - var month = Math.floor(monthly); - return (month + 1).toFixed(0) + "/" + Math.ceil(0.5 + (monthly - month)*Math.ceil(numDaysPerMonth)).toFixed(0); } -); - -panel.newSlider("Day Time", 0, 24, - - function(value) { Scene.setStageDayTime(value); panel.update("Light Direction"); }, - function() { return Scene.getStageDayTime(); }, - function(value) { - var hour = Math.floor(value); - return (hour).toFixed(0) + ":" + ((value - hour)*60.0).toFixed(0); - } -); - -var tickTackPeriod = 50; -var tickTackSpeed = 0.0; -panel.newSlider("Tick tack time", -1.0, 1.0, - function(value) { tickTackSpeed = value; }, - function() { return tickTackSpeed; }, - function(value) { return (value).toFixed(2); } -); - -function runStageTime() { - if (tickTackSpeed != 0.0) { - var hour = panel.get("Day Time"); - hour += tickTackSpeed; - panel.set("Day Time", hour); - - if (hour >= 24.0) { - panel.set("Year Time", panel.get("Year Time") + 1); - } else if (hour < 0.0) { - panel.set("Year Time", panel.get("Year Time") - 1); - } - } -} -Script.setInterval(runStageTime, tickTackPeriod); - -panel.newCheckbox("Enable Sun Model", - function(value) { Scene.setStageSunModelEnable((value != 0)); }, - function() { return Scene.isStageSunModelEnabled(); }, - function(value) { return (value); } -); - -panel.newDirectionBox("Light Direction", - function(value) { Scene.setKeyLightDirection(value); }, - function() { return Scene.getKeyLightDirection(); }, - function(value) { return value.x.toFixed(2) + "," + value.y.toFixed(2) + "," + value.z.toFixed(2); } -); - -panel.newSlider("Light Intensity", 0.0, 5, - function(value) { Scene.setKeyLightIntensity(value); }, - function() { return Scene.getKeyLightIntensity(); }, - function(value) { return (value).toFixed(2); } -); - -panel.newSlider("Ambient Light Intensity", 0.0, 1.0, - function(value) { Scene.setKeyLightAmbientIntensity(value); }, - function() { return Scene.getKeyLightAmbientIntensity(); }, - function(value) { return (value).toFixed(2); } -); - -panel.newColorBox("Light Color", - function(value) { Scene.setKeyLightColor(value); }, - function() { return Scene.getKeyLightColor(); }, - function(value) { return (value); } // "(" + value.x + "," = value.y + "," + value.z + ")"; } -); - -Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); -Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); -Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); - -function scriptEnding() { - Menu.removeMenu("Developer > Scene"); - panel.destroy(); -} -Script.scriptEnding.connect(scriptEnding); +// +// SunLightExample.js +// examples +// Sam Gateau +// 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("../../utilities/tools/cookies.js"); + +var panel = new Panel(10, 400); + +panel.newSlider("Origin Longitude", -180, 180, + function(value) { Scene.setStageLocation(value, Scene.getStageLocationLatitude(), Scene.getStageLocationAltitude()); }, + function() { return Scene.getStageLocationLongitude(); }, + function(value) { return value.toFixed(0) + " deg"; } +); + +panel.newSlider("Origin Latitude", -90, 90, + function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), value, Scene.getStageLocationAltitude()); }, + function() { return Scene.getStageLocationLatitude(); }, + function(value) { return value.toFixed(0) + " deg"; } +); + +panel.newSlider("Origin Altitude", 0, 1000, + function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), Scene.getStageLocationLatitude(), value); }, + function() { return Scene.getStageLocationAltitude(); }, + function(value) { return (value).toFixed(0) + " km"; } +); + +panel.newSlider("Year Time", 0, 364, + function(value) { Scene.setStageYearTime(value); }, + function() { return Scene.getStageYearTime(); }, + function(value) { + var numDaysPerMonth = 365.0 / 12.0; + var monthly = (value / numDaysPerMonth); + var month = Math.floor(monthly); + return (month + 1).toFixed(0) + "/" + Math.ceil(0.5 + (monthly - month)*Math.ceil(numDaysPerMonth)).toFixed(0); } +); + +panel.newSlider("Day Time", 0, 24, + + function(value) { Scene.setStageDayTime(value); panel.update("Light Direction"); }, + function() { return Scene.getStageDayTime(); }, + function(value) { + var hour = Math.floor(value); + return (hour).toFixed(0) + ":" + ((value - hour)*60.0).toFixed(0); + } +); + +var tickTackPeriod = 50; +var tickTackSpeed = 0.0; +panel.newSlider("Tick tack time", -1.0, 1.0, + function(value) { tickTackSpeed = value; }, + function() { return tickTackSpeed; }, + function(value) { return (value).toFixed(2); } +); + +function runStageTime() { + if (tickTackSpeed != 0.0) { + var hour = panel.get("Day Time"); + hour += tickTackSpeed; + panel.set("Day Time", hour); + + if (hour >= 24.0) { + panel.set("Year Time", panel.get("Year Time") + 1); + } else if (hour < 0.0) { + panel.set("Year Time", panel.get("Year Time") - 1); + } + } +} +Script.setInterval(runStageTime, tickTackPeriod); + +panel.newCheckbox("Enable Sun Model", + function(value) { Scene.setStageSunModelEnable((value != 0)); }, + function() { return Scene.isStageSunModelEnabled(); }, + function(value) { return (value); } +); + +panel.newDirectionBox("Light Direction", + function(value) { Scene.setKeyLightDirection(value); }, + function() { return Scene.getKeyLightDirection(); }, + function(value) { return value.x.toFixed(2) + "," + value.y.toFixed(2) + "," + value.z.toFixed(2); } +); + +panel.newSlider("Light Intensity", 0.0, 5, + function(value) { Scene.setKeyLightIntensity(value); }, + function() { return Scene.getKeyLightIntensity(); }, + function(value) { return (value).toFixed(2); } +); + +panel.newSlider("Ambient Light Intensity", 0.0, 1.0, + function(value) { Scene.setKeyLightAmbientIntensity(value); }, + function() { return Scene.getKeyLightAmbientIntensity(); }, + function(value) { return (value).toFixed(2); } +); + +panel.newColorBox("Light Color", + function(value) { Scene.setKeyLightColor(value); }, + function() { return Scene.getKeyLightColor(); }, + function(value) { return (value); } // "(" + value.x + "," = value.y + "," + value.z + ")"; } +); + +Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); +Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); +Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); + +function scriptEnding() { + Menu.removeMenu("Developer > Scene"); + panel.destroy(); +} +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/leaves.js b/examples/leaves.js index 4610cd2ef0..40398822c2 100755 --- a/examples/leaves.js +++ b/examples/leaves.js @@ -1,331 +1,331 @@ -// -// Leaves.js -// examples -// -// Created by Bing Shearer on 14 Jul 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 -// -var leafName = "scriptLeaf"; -var leafSquall = function (properties) { - var // Properties - squallOrigin, - squallRadius, - leavesPerMinute = 60, - leafSize = { - x: 0.1, - y: 0.1, - z: 0.1 - }, - leafFallSpeed = 1, // m/s - leafLifetime = 60, // Seconds - leafSpinMax = 0, // Maximum angular velocity per axis; deg/s - debug = false, // Display origin circle; don't use running on Stack Manager - // Other - squallCircle, - SQUALL_CIRCLE_COLOR = { - red: 255, - green: 0, - blue: 0 - }, - SQUALL_CIRCLE_ALPHA = 0.5, - SQUALL_CIRCLE_ROTATION = Quat.fromPitchYawRollDegrees(90, 0, 0), - leafProperties, - leaf_MODEL_URL = "https://hifi-public.s3.amazonaws.com/ozan/support/forBing/palmLeaf.fbx", - leafTimer, - leaves = [], // HACK: Work around leaves not always getting velocities - leafVelocities = [], // HACK: Work around leaves not always getting velocities - DEGREES_TO_RADIANS = Math.PI / 180, - leafDeleteOnTearDown = true, - maxLeaves, - leafCount, - nearbyEntities, - complexMovement = false, - movementTime = 0, - maxSpinRadians = properties.leafSpinMax * DEGREES_TO_RADIANS, - windFactor, - leafDeleteOnGround = false, - floorHeight = null; - - - function processProperties() { - if (!properties.hasOwnProperty("origin")) { - print("ERROR: Leaf squall origin must be specified"); - return; - } - squallOrigin = properties.origin; - - if (!properties.hasOwnProperty("radius")) { - print("ERROR: Leaf squall radius must be specified"); - return; - } - squallRadius = properties.radius; - - if (properties.hasOwnProperty("leavesPerMinute")) { - leavesPerMinute = properties.leavesPerMinute; - } - - if (properties.hasOwnProperty("leafSize")) { - leafSize = properties.leafSize; - } - - if (properties.hasOwnProperty("leafFallSpeed")) { - leafFallSpeed = properties.leafFallSpeed; - } - - if (properties.hasOwnProperty("leafLifetime")) { - leafLifetime = properties.leafLifetime; - } - - if (properties.hasOwnProperty("leafSpinMax")) { - leafSpinMax = properties.leafSpinMax; - } - - if (properties.hasOwnProperty("debug")) { - debug = properties.debug; - } - if (properties.hasOwnProperty("floorHeight")) { - floorHeight = properties.floorHeight; - } - if (properties.hasOwnProperty("maxLeaves")) { - maxLeaves = properties.maxLeaves; - } - if (properties.hasOwnProperty("complexMovement")) { - complexMovement = properties.complexMovement; - } - if (properties.hasOwnProperty("leafDeleteOnGround")) { - leafDeleteOnGround = properties.leafDeleteOnGround; - } - if (properties.hasOwnProperty("windFactor")) { - windFactor = properties.windFactor; - } else if (complexMovement == true){ - print("ERROR: Wind Factor must be defined for complex movement") - } - - leafProperties = { - type: "Model", - name: leafName, - modelURL: leaf_MODEL_URL, - lifetime: leafLifetime, - dimensions: leafSize, - velocity: { - x: 0, - y: -leafFallSpeed, - z: 0 - }, - damping: 0, - angularDamping: 0, - ignoreForCollisions: true - - }; - } - - function createleaf() { - var angle, - radius, - offset, - leaf, - spin = { - x: 0, - y: 0, - z: 0 - }, - i; - - // HACK: Work around leaves not always getting velocities set at creation - for (i = 0; i < leaves.length; i++) { - Entities.editEntity(leaves[i], leafVelocities[i]); - } - - angle = Math.random() * leafSpinMax; - radius = Math.random() * squallRadius; - offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, angle, 0), { - x: 0, - y: -0.1, - z: radius - }); - leafProperties.position = Vec3.sum(squallOrigin, offset); - if (properties.leafSpinMax > 0 && !complexMovement) { - spin = { - x: Math.random() * maxSpinRadians, - y: Math.random() * maxSpinRadians, - z: Math.random() * maxSpinRadians - }; - leafProperties.angularVelocity = spin; - } else if (complexMovement) { - spin = { - x: 0, - y: 0, - z: 0 - }; - leafProperties.angularVelocity = spin - } - leaf = Entities.addEntity(leafProperties); - - // HACK: Work around leaves not always getting velocities set at creation - leaves.push(leaf); - leafVelocities.push({ - velocity: leafProperties.velocity, - angularVelocity: spin - }); - if (leaves.length > 5) { - leaves.shift(); - leafVelocities.shift(); - } - } - - function setUp() { - if (debug) { - squallCircle = Overlays.addOverlay("circle3d", { - size: { - x: 2 * squallRadius, - y: 2 * squallRadius - }, - color: SQUALL_CIRCLE_COLOR, - alpha: SQUALL_CIRCLE_ALPHA, - solid: true, - visible: debug, - position: squallOrigin, - rotation: SQUALL_CIRCLE_ROTATION - }); - } - - leafTimer = Script.setInterval(function () { - if (leafCount <= maxLeaves - 1) { - createleaf() - } - }, 60000 / leavesPerMinute); - } - Script.setInterval(function () { - nearbyEntities = Entities.findEntities(squallOrigin, squallRadius); - newLeafMovement() - }, 100); - - function newLeafMovement() { //new additions to leaf code. Operates at 10 Hz or every 100 ms - movementTime += 0.1; - var currentLeaf, - randomRotationSpeed = { - x: maxSpinRadians * Math.sin(movementTime), - y: maxSpinRadians * Math.random(), - z: maxSpinRadians * Math.sin(movementTime / 7) - }; - for (var i = 0; i < nearbyEntities.length; i++) { - var entityProperties = Entities.getEntityProperties(nearbyEntities[i]); - var entityName = entityProperties.name; - if (leafName === entityName) { - currentLeaf = nearbyEntities[i]; - var leafHeight = entityProperties.position.y; - if (complexMovement && leafHeight > floorHeight || complexMovement && floorHeight == null) { //actual new movement code; - var leafCurrentVel = entityProperties.velocity, - leafCurrentRot = entityProperties.rotation, - yVec = { - x: 0, - y: 1, - z: 0 - }, - leafYinWFVec = Vec3.multiplyQbyV(leafCurrentRot, yVec), - leafLocalHorVec = Vec3.cross(leafYinWFVec, yVec), - leafMostDownVec = Vec3.cross(leafYinWFVec, leafLocalHorVec), - leafDesiredVel = Vec3.multiply(leafMostDownVec, windFactor), - leafVelDelt = Vec3.subtract(leafDesiredVel, leafCurrentVel), - leafNewVel = Vec3.sum(leafCurrentVel, Vec3.multiply(leafVelDelt, windFactor)); - Entities.editEntity(currentLeaf, { - angularVelocity: randomRotationSpeed, - velocity: leafNewVel - }) - } else if (leafHeight <= floorHeight) { - if (!leafDeleteOnGround) { - Entities.editEntity(nearbyEntities[i], { - locked: false, - velocity: { - x: 0, - y: 0, - z: 0 - }, - angularVelocity: { - x: 0, - y: 0, - z: 0 - } - }) - } else { - Entity.deleteEntity(currentLeaf); - } - } - } - } - } - - - - getLeafCount = Script.setInterval(function () { - leafCount = 0 - for (var i = 0; i < nearbyEntities.length; i++) { - var entityName = Entities.getEntityProperties(nearbyEntities[i]).name; - //Stop Leaves at floorHeight - if (leafName === entityName) { - leafCount++; - if (i == nearbyEntities.length - 1) { - //print(leafCount); - } - } - } - }, 1000) - - - - function tearDown() { - Script.clearInterval(leafTimer); - Overlays.deleteOverlay(squallCircle); - if (leafDeleteOnTearDown) { - for (var i = 0; i < nearbyEntities.length; i++) { - var entityName = Entities.getEntityProperties(nearbyEntities[i]).name; - if (leafName === entityName) { - //We have a match - delete this entity - Entities.editEntity(nearbyEntities[i], { - locked: false - }); - Entities.deleteEntity(nearbyEntities[i]); - } - } - } - } - - - - processProperties(); - setUp(); - Script.scriptEnding.connect(tearDown); - - return {}; -}; - -var leafSquall1 = new leafSquall({ - origin: { - x: 3071.5, - y: 2170, - z: 6765.3 - }, - radius: 100, - leavesPerMinute: 30, - leafSize: { - x: 0.3, - y: 0.00, - z: 0.3 - }, - leafFallSpeed: 0.4, - leafLifetime: 100, - leafSpinMax: 30, - debug: false, - maxLeaves: 100, - leafDeleteOnTearDown: true, - complexMovement: true, - floorHeight: 2143.5, - windFactor: 0.5, - leafDeleteOnGround: false -}); - -// todo -//deal with depth issue \ No newline at end of file +// +// Leaves.js +// examples +// +// Created by Bing Shearer on 14 Jul 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 +// +var leafName = "scriptLeaf"; +var leafSquall = function (properties) { + var // Properties + squallOrigin, + squallRadius, + leavesPerMinute = 60, + leafSize = { + x: 0.1, + y: 0.1, + z: 0.1 + }, + leafFallSpeed = 1, // m/s + leafLifetime = 60, // Seconds + leafSpinMax = 0, // Maximum angular velocity per axis; deg/s + debug = false, // Display origin circle; don't use running on Stack Manager + // Other + squallCircle, + SQUALL_CIRCLE_COLOR = { + red: 255, + green: 0, + blue: 0 + }, + SQUALL_CIRCLE_ALPHA = 0.5, + SQUALL_CIRCLE_ROTATION = Quat.fromPitchYawRollDegrees(90, 0, 0), + leafProperties, + leaf_MODEL_URL = "https://hifi-public.s3.amazonaws.com/ozan/support/forBing/palmLeaf.fbx", + leafTimer, + leaves = [], // HACK: Work around leaves not always getting velocities + leafVelocities = [], // HACK: Work around leaves not always getting velocities + DEGREES_TO_RADIANS = Math.PI / 180, + leafDeleteOnTearDown = true, + maxLeaves, + leafCount, + nearbyEntities, + complexMovement = false, + movementTime = 0, + maxSpinRadians = properties.leafSpinMax * DEGREES_TO_RADIANS, + windFactor, + leafDeleteOnGround = false, + floorHeight = null; + + + function processProperties() { + if (!properties.hasOwnProperty("origin")) { + print("ERROR: Leaf squall origin must be specified"); + return; + } + squallOrigin = properties.origin; + + if (!properties.hasOwnProperty("radius")) { + print("ERROR: Leaf squall radius must be specified"); + return; + } + squallRadius = properties.radius; + + if (properties.hasOwnProperty("leavesPerMinute")) { + leavesPerMinute = properties.leavesPerMinute; + } + + if (properties.hasOwnProperty("leafSize")) { + leafSize = properties.leafSize; + } + + if (properties.hasOwnProperty("leafFallSpeed")) { + leafFallSpeed = properties.leafFallSpeed; + } + + if (properties.hasOwnProperty("leafLifetime")) { + leafLifetime = properties.leafLifetime; + } + + if (properties.hasOwnProperty("leafSpinMax")) { + leafSpinMax = properties.leafSpinMax; + } + + if (properties.hasOwnProperty("debug")) { + debug = properties.debug; + } + if (properties.hasOwnProperty("floorHeight")) { + floorHeight = properties.floorHeight; + } + if (properties.hasOwnProperty("maxLeaves")) { + maxLeaves = properties.maxLeaves; + } + if (properties.hasOwnProperty("complexMovement")) { + complexMovement = properties.complexMovement; + } + if (properties.hasOwnProperty("leafDeleteOnGround")) { + leafDeleteOnGround = properties.leafDeleteOnGround; + } + if (properties.hasOwnProperty("windFactor")) { + windFactor = properties.windFactor; + } else if (complexMovement == true){ + print("ERROR: Wind Factor must be defined for complex movement") + } + + leafProperties = { + type: "Model", + name: leafName, + modelURL: leaf_MODEL_URL, + lifetime: leafLifetime, + dimensions: leafSize, + velocity: { + x: 0, + y: -leafFallSpeed, + z: 0 + }, + damping: 0, + angularDamping: 0, + ignoreForCollisions: true + + }; + } + + function createleaf() { + var angle, + radius, + offset, + leaf, + spin = { + x: 0, + y: 0, + z: 0 + }, + i; + + // HACK: Work around leaves not always getting velocities set at creation + for (i = 0; i < leaves.length; i++) { + Entities.editEntity(leaves[i], leafVelocities[i]); + } + + angle = Math.random() * leafSpinMax; + radius = Math.random() * squallRadius; + offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, angle, 0), { + x: 0, + y: -0.1, + z: radius + }); + leafProperties.position = Vec3.sum(squallOrigin, offset); + if (properties.leafSpinMax > 0 && !complexMovement) { + spin = { + x: Math.random() * maxSpinRadians, + y: Math.random() * maxSpinRadians, + z: Math.random() * maxSpinRadians + }; + leafProperties.angularVelocity = spin; + } else if (complexMovement) { + spin = { + x: 0, + y: 0, + z: 0 + }; + leafProperties.angularVelocity = spin + } + leaf = Entities.addEntity(leafProperties); + + // HACK: Work around leaves not always getting velocities set at creation + leaves.push(leaf); + leafVelocities.push({ + velocity: leafProperties.velocity, + angularVelocity: spin + }); + if (leaves.length > 5) { + leaves.shift(); + leafVelocities.shift(); + } + } + + function setUp() { + if (debug) { + squallCircle = Overlays.addOverlay("circle3d", { + size: { + x: 2 * squallRadius, + y: 2 * squallRadius + }, + color: SQUALL_CIRCLE_COLOR, + alpha: SQUALL_CIRCLE_ALPHA, + solid: true, + visible: debug, + position: squallOrigin, + rotation: SQUALL_CIRCLE_ROTATION + }); + } + + leafTimer = Script.setInterval(function () { + if (leafCount <= maxLeaves - 1) { + createleaf() + } + }, 60000 / leavesPerMinute); + } + Script.setInterval(function () { + nearbyEntities = Entities.findEntities(squallOrigin, squallRadius); + newLeafMovement() + }, 100); + + function newLeafMovement() { //new additions to leaf code. Operates at 10 Hz or every 100 ms + movementTime += 0.1; + var currentLeaf, + randomRotationSpeed = { + x: maxSpinRadians * Math.sin(movementTime), + y: maxSpinRadians * Math.random(), + z: maxSpinRadians * Math.sin(movementTime / 7) + }; + for (var i = 0; i < nearbyEntities.length; i++) { + var entityProperties = Entities.getEntityProperties(nearbyEntities[i]); + var entityName = entityProperties.name; + if (leafName === entityName) { + currentLeaf = nearbyEntities[i]; + var leafHeight = entityProperties.position.y; + if (complexMovement && leafHeight > floorHeight || complexMovement && floorHeight == null) { //actual new movement code; + var leafCurrentVel = entityProperties.velocity, + leafCurrentRot = entityProperties.rotation, + yVec = { + x: 0, + y: 1, + z: 0 + }, + leafYinWFVec = Vec3.multiplyQbyV(leafCurrentRot, yVec), + leafLocalHorVec = Vec3.cross(leafYinWFVec, yVec), + leafMostDownVec = Vec3.cross(leafYinWFVec, leafLocalHorVec), + leafDesiredVel = Vec3.multiply(leafMostDownVec, windFactor), + leafVelDelt = Vec3.subtract(leafDesiredVel, leafCurrentVel), + leafNewVel = Vec3.sum(leafCurrentVel, Vec3.multiply(leafVelDelt, windFactor)); + Entities.editEntity(currentLeaf, { + angularVelocity: randomRotationSpeed, + velocity: leafNewVel + }) + } else if (leafHeight <= floorHeight) { + if (!leafDeleteOnGround) { + Entities.editEntity(nearbyEntities[i], { + locked: false, + velocity: { + x: 0, + y: 0, + z: 0 + }, + angularVelocity: { + x: 0, + y: 0, + z: 0 + } + }) + } else { + Entity.deleteEntity(currentLeaf); + } + } + } + } + } + + + + getLeafCount = Script.setInterval(function () { + leafCount = 0 + for (var i = 0; i < nearbyEntities.length; i++) { + var entityName = Entities.getEntityProperties(nearbyEntities[i]).name; + //Stop Leaves at floorHeight + if (leafName === entityName) { + leafCount++; + if (i == nearbyEntities.length - 1) { + //print(leafCount); + } + } + } + }, 1000) + + + + function tearDown() { + Script.clearInterval(leafTimer); + Overlays.deleteOverlay(squallCircle); + if (leafDeleteOnTearDown) { + for (var i = 0; i < nearbyEntities.length; i++) { + var entityName = Entities.getEntityProperties(nearbyEntities[i]).name; + if (leafName === entityName) { + //We have a match - delete this entity + Entities.editEntity(nearbyEntities[i], { + locked: false + }); + Entities.deleteEntity(nearbyEntities[i]); + } + } + } + } + + + + processProperties(); + setUp(); + Script.scriptEnding.connect(tearDown); + + return {}; +}; + +var leafSquall1 = new leafSquall({ + origin: { + x: 3071.5, + y: 2170, + z: 6765.3 + }, + radius: 100, + leavesPerMinute: 30, + leafSize: { + x: 0.3, + y: 0.00, + z: 0.3 + }, + leafFallSpeed: 0.4, + leafLifetime: 100, + leafSpinMax: 30, + debug: false, + maxLeaves: 100, + leafDeleteOnTearDown: true, + complexMovement: true, + floorHeight: 2143.5, + windFactor: 0.5, + leafDeleteOnGround: false +}); + +// todo +//deal with depth issue diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index d1192deee7..2a4471bfbf 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -1,943 +1,943 @@ -// -// walkApi.js -// version 1.3 -// -// Created by David Wooldridge, June 2015 -// Copyright © 2014 - 2015 High Fidelity, Inc. -// -// Exposes API for use by walk.js version 1.2+. -// -// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// included here to ensure walkApi.js can be used as an API, separate from walk.js -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; - } - } - // settings - this.headFree = true; - this.armsFree = this.hydraCheck(); // automatically sets true to enable Hydra support - temporary fix - this.makesFootStepSounds = false; - this.blenderPreRotations = false; // temporary fix - this.animationSet = undefined; // currently just one animation set - this.setAnimationSet = function(animationSet) { - this.animationSet = animationSet; - switch (animationSet) { - case 'standardMale': - this.selectedIdle = walkAssets.getAnimationDataFile("MaleIdle"); - this.selectedWalk = walkAssets.getAnimationDataFile("MaleWalk"); - this.selectedWalkBackwards = walkAssets.getAnimationDataFile("MaleWalkBackwards"); - this.selectedSideStepLeft = walkAssets.getAnimationDataFile("MaleSideStepLeft"); - this.selectedSideStepRight = walkAssets.getAnimationDataFile("MaleSideStepRight"); - this.selectedWalkBlend = walkAssets.getAnimationDataFile("WalkBlend"); - this.selectedHover = walkAssets.getAnimationDataFile("MaleHover"); - this.selectedFly = walkAssets.getAnimationDataFile("MaleFly"); - this.selectedFlyBackwards = walkAssets.getAnimationDataFile("MaleFlyBackwards"); - this.selectedFlyDown = walkAssets.getAnimationDataFile("MaleFlyDown"); - this.selectedFlyUp = walkAssets.getAnimationDataFile("MaleFlyUp"); - this.selectedFlyBlend = walkAssets.getAnimationDataFile("FlyBlend"); - this.currentAnimation = this.selectedIdle; - return; - } - } - this.setAnimationSet('standardMale'); - - // calibration - this.calibration = { - hipsToFeet: 1, - strideLength: this.selectedWalk.calibration.strideLength - } - this.distanceFromSurface = 0; - this.calibrate = function() { - // Triple check: measurements are taken three times to ensure accuracy - the first result is often too large - const MAX_ATTEMPTS = 3; - var attempts = MAX_ATTEMPTS; - var extraAttempts = 0; - do { - for (joint in walkAssets.animationReference.joints) { - var IKChain = walkAssets.animationReference.joints[joint].IKChain; - - // only need to zero right leg IK chain and hips - if (IKChain === "RightLeg" || joint === "Hips" ) { - MyAvatar.setJointRotation(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); - } - } - this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; - - // maybe measuring before Blender pre-rotations have been applied? - if (this.calibration.hipsToFeet < 0 && this.blenderPreRotations) { - this.calibration.hipsToFeet *= -1; - } - - if (this.calibration.hipsToFeet === 0 && extraAttempts < 100) { - attempts++; - extraAttempts++;// Interface can sometimes report zero for hips to feet. if so, we try again. - } - } while (attempts-- > 1) - - // just in case - if (this.calibration.hipsToFeet <= 0 || isNaN(this.calibration.hipsToFeet)) { - this.calibration.hipsToFeet = 1; - print('walk.js error: Unable to get a non-zero measurement for the avatar hips to feet measure. Hips to feet set to default value ('+ - this.calibration.hipsToFeet.toFixed(3)+'m). This will cause some foot sliding. If your avatar has only just appeared, it is recommended that you re-load the walk script.'); - } else { - print('walk.js info: Hips to feet calibrated to '+this.calibration.hipsToFeet.toFixed(3)+'m'); - } - } - - // pose the fingers - this.poseFingers = function() { - for (knuckle in walkAssets.animationReference.leftHand) { - if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { - MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); - } else { - MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); - } - } - for (knuckle in walkAssets.animationReference.rightHand) { - if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { - MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); - } else { - MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); - } - } - }; - this.calibrate(); - this.poseFingers(); - - // footsteps - this.nextStep = RIGHT; // the first step is right, because the waveforms say so - this.leftAudioInjector = null; - this.rightAudioInjector = null; - this.makeFootStepSound = function() { - // correlate footstep volume with avatar speed. place the audio source at the feet, not the hips - const SPEED_THRESHOLD = 0.4; - const VOLUME_ATTENUATION = 0.8; - const MIN_VOLUME = 0.5; - var volume = Vec3.length(motion.velocity) > SPEED_THRESHOLD ? - VOLUME_ATTENUATION * Vec3.length(motion.velocity) / MAX_WALK_SPEED : MIN_VOLUME; - volume = volume > 1 ? 1 : volume; // occurs when landing at speed - can walk faster than max walking speed - var options = { - position: Vec3.sum(MyAvatar.position, {x:0, y: -this.calibration.hipsToFeet, z:0}), - volume: volume - }; - if (this.nextStep === RIGHT) { - if (this.rightAudioInjector === null) { - this.rightAudioInjector = Audio.playSound(walkAssets.footsteps[0], options); - } else { - this.rightAudioInjector.setOptions(options); - this.rightAudioInjector.restart(); - } - this.nextStep = LEFT; - } else if (this.nextStep === LEFT) { - if (this.leftAudioInjector === null) { - this.leftAudioInjector = Audio.playSound(walkAssets.footsteps[1], options); - } else { - this.leftAudioInjector.setOptions(options); - this.leftAudioInjector.restart(); - } - this.nextStep = RIGHT; - } - } -}; - -// constructor for the Motion object -Motion = function() { - this.isLive = true; - // locomotion status - this.state = STATIC; - this.nextState = STATIC; - this.isMoving = false; - this.isWalkingSpeed = false; - this.isFlyingSpeed = false; - this.isAccelerating = false; - this.isDecelerating = false; - this.isDeceleratingFast = false; - this.isComingToHalt = false; - this.directedAcceleration = 0; - - // used to make sure at least one step has been taken when transitioning from a walk cycle - this.elapsedFTDegrees = 0; - - // the current transition (any layered transitions are nested within this transition) - this.currentTransition = null; - - // orientation, locomotion and timing - this.velocity = {x:0, y:0, z:0}; - this.acceleration = {x:0, y:0, z:0}; - this.yaw = Quat.safeEulerAngles(MyAvatar.orientation).y; - this.yawDelta = 0; - this.yawDeltaAcceleration = 0; - this.direction = FORWARDS; - this.deltaTime = 0; - - // historical orientation, locomotion and timing - this.lastDirection = FORWARDS; - this.lastVelocity = {x:0, y:0, z:0}; - this.lastYaw = Quat.safeEulerAngles(MyAvatar.orientation).y; - this.lastYawDelta = 0; - this.lastYawDeltaAcceleration = 0; - - // Quat.safeEulerAngles(MyAvatar.orientation).y tends to repeat values between frames, so values are filtered - var YAW_SMOOTHING = 22; - this.yawFilter = filter.createAveragingFilter(YAW_SMOOTHING); - this.deltaTimeFilter = filter.createAveragingFilter(YAW_SMOOTHING); - this.yawDeltaAccelerationFilter = filter.createAveragingFilter(YAW_SMOOTHING); - - // assess locomotion state - this.assess = function(deltaTime) { - // calculate avatar frame speed, velocity and acceleration - this.deltaTime = deltaTime; - this.velocity = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), MyAvatar.getVelocity()); - var lateralVelocity = Math.sqrt(Math.pow(this.velocity.x, 2) + Math.pow(this.velocity.z, 2)); - - // MyAvatar.getAcceleration() currently not working. bug report submitted: https://worklist.net/20527 - var acceleration = {x:0, y:0, z:0}; - this.acceleration.x = (this.velocity.x - this.lastVelocity.x) / deltaTime; - this.acceleration.y = (this.velocity.y - this.lastVelocity.y) / deltaTime; - this.acceleration.z = (this.velocity.z - this.lastVelocity.z) / deltaTime; - - // MyAvatar.getAngularVelocity and MyAvatar.getAngularAcceleration currently not working. bug report submitted - this.yaw = Quat.safeEulerAngles(MyAvatar.orientation).y; - if (this.lastYaw < 0 && this.yaw > 0 || this.lastYaw > 0 && this.yaw < 0) { - this.lastYaw *= -1; - } - var timeDelta = this.deltaTimeFilter.process(deltaTime); - this.yawDelta = filter.degToRad(this.yawFilter.process(this.lastYaw - this.yaw)) / timeDelta; - this.yawDeltaAcceleration = this.yawDeltaAccelerationFilter.process(this.lastYawDelta - this.yawDelta) / timeDelta; - - // how far above the surface is the avatar? (for testing / validation purposes) - var pickRay = {origin: MyAvatar.position, direction: {x:0, y:-1, z:0}}; - var distanceFromSurface = Entities.findRayIntersectionBlocking(pickRay).distance; - avatar.distanceFromSurface = distanceFromSurface - avatar.calibration.hipsToFeet; - - // determine principle direction of locomotion - var FWD_BACK_BIAS = 100; // helps prevent false sidestep condition detection when banking hard - if (Math.abs(this.velocity.x) > Math.abs(this.velocity.y) && - Math.abs(this.velocity.x) > FWD_BACK_BIAS * Math.abs(this.velocity.z)) { - if (this.velocity.x < 0) { - this.directedAcceleration = -this.acceleration.x; - this.direction = LEFT; - } else if (this.velocity.x > 0){ - this.directedAcceleration = this.acceleration.x; - this.direction = RIGHT; - } - } else if (Math.abs(this.velocity.y) > Math.abs(this.velocity.x) && - Math.abs(this.velocity.y) > Math.abs(this.velocity.z)) { - if (this.velocity.y > 0) { - this.directedAcceleration = this.acceleration.y; - this.direction = UP; - } else if (this.velocity.y < 0) { - this.directedAcceleration = -this.acceleration.y; - this.direction = DOWN; - } - } else if (FWD_BACK_BIAS * Math.abs(this.velocity.z) > Math.abs(this.velocity.x) && - Math.abs(this.velocity.z) > Math.abs(this.velocity.y)) { - if (this.velocity.z < 0) { - this.direction = FORWARDS; - this.directedAcceleration = -this.acceleration.z; - } else if (this.velocity.z > 0) { - this.directedAcceleration = this.acceleration.z; - this.direction = BACKWARDS; - } - } else { - this.direction = NONE; - this.directedAcceleration = 0; - } - - // set speed flags - if (Vec3.length(this.velocity) < MOVE_THRESHOLD) { - this.isMoving = false; - this.isWalkingSpeed = false; - this.isFlyingSpeed = false; - this.isComingToHalt = false; - } else if (Vec3.length(this.velocity) < MAX_WALK_SPEED) { - this.isMoving = true; - this.isWalkingSpeed = true; - this.isFlyingSpeed = false; - } else { - this.isMoving = true; - this.isWalkingSpeed = false; - this.isFlyingSpeed = true; - } - - // set acceleration flags - if (this.directedAcceleration > ACCELERATION_THRESHOLD) { - this.isAccelerating = true; - this.isDecelerating = false; - this.isDeceleratingFast = false; - this.isComingToHalt = false; - } else if (this.directedAcceleration < DECELERATION_THRESHOLD) { - this.isAccelerating = false; - this.isDecelerating = true; - this.isDeceleratingFast = (this.directedAcceleration < FAST_DECELERATION_THRESHOLD); - } else { - this.isAccelerating = false; - this.isDecelerating = false; - this.isDeceleratingFast = false; - } - - // use the gathered information to build up some spatial awareness - var isOnSurface = (avatar.distanceFromSurface < ON_SURFACE_THRESHOLD); - var isUnderGravity = (avatar.distanceFromSurface < GRAVITY_THRESHOLD); - var isTakingOff = (isUnderGravity && this.velocity.y > OVERCOME_GRAVITY_SPEED); - var isComingInToLand = (isUnderGravity && this.velocity.y < -OVERCOME_GRAVITY_SPEED); - var aboutToLand = isComingInToLand && avatar.distanceFromSurface < LANDING_THRESHOLD; - var surfaceMotion = isOnSurface && this.isMoving; - var acceleratingAndAirborne = this.isAccelerating && !isOnSurface; - var goingTooFastToWalk = !this.isDecelerating && this.isFlyingSpeed; - var movingDirectlyUpOrDown = (this.direction === UP || this.direction === DOWN) - var maybeBouncing = Math.abs(this.acceleration.y > BOUNCE_ACCELERATION_THRESHOLD) ? true : false; - - // we now have enough information to set the appropriate locomotion mode - switch (this.state) { - case STATIC: - var staticToAirMotion = this.isMoving && (acceleratingAndAirborne || goingTooFastToWalk || - (movingDirectlyUpOrDown && !isOnSurface)); - var staticToSurfaceMotion = surfaceMotion && !motion.isComingToHalt && !movingDirectlyUpOrDown && - !this.isDecelerating && lateralVelocity > MOVE_THRESHOLD; - - if (staticToAirMotion) { - this.nextState = AIR_MOTION; - } else if (staticToSurfaceMotion) { - this.nextState = SURFACE_MOTION; - } else { - this.nextState = STATIC; - } - break; - - case SURFACE_MOTION: - var surfaceMotionToStatic = !this.isMoving || - (this.isDecelerating && motion.lastDirection !== DOWN && surfaceMotion && - !maybeBouncing && Vec3.length(this.velocity) < MAX_WALK_SPEED); - var surfaceMotionToAirMotion = (acceleratingAndAirborne || goingTooFastToWalk || movingDirectlyUpOrDown) && - (!surfaceMotion && isTakingOff) || - (!surfaceMotion && this.isMoving && !isComingInToLand); - - if (surfaceMotionToStatic) { - // working on the assumption that stopping is now inevitable - if (!motion.isComingToHalt && isOnSurface) { - motion.isComingToHalt = true; - } - this.nextState = STATIC; - } else if (surfaceMotionToAirMotion) { - this.nextState = AIR_MOTION; - } else { - this.nextState = SURFACE_MOTION; - } - break; - - case AIR_MOTION: - var airMotionToSurfaceMotion = (surfaceMotion || aboutToLand) && !movingDirectlyUpOrDown; - var airMotionToStatic = !this.isMoving && this.direction === this.lastDirection; - - if (airMotionToSurfaceMotion){ - this.nextState = SURFACE_MOTION; - } else if (airMotionToStatic) { - this.nextState = STATIC; - } else { - this.nextState = AIR_MOTION; - } - break; - } - } - - // frequency time wheel (foot / ground speed matching) - const DEFAULT_HIPS_TO_FEET = 1; - this.frequencyTimeWheelPos = 0; - this.frequencyTimeWheelRadius = DEFAULT_HIPS_TO_FEET / 2; - this.recentFrequencyTimeIncrements = []; - const FT_WHEEL_HISTORY_LENGTH = 8; - for (var i = 0; i < FT_WHEEL_HISTORY_LENGTH; i++) { - this.recentFrequencyTimeIncrements.push(0); - } - this.averageFrequencyTimeIncrement = 0; - - this.advanceFrequencyTimeWheel = function(angle){ - this.elapsedFTDegrees += angle; - // keep a running average of increments for use in transitions (used during transitioning) - this.recentFrequencyTimeIncrements.push(angle); - this.recentFrequencyTimeIncrements.shift(); - for (increment in this.recentFrequencyTimeIncrements) { - this.averageFrequencyTimeIncrement += this.recentFrequencyTimeIncrements[increment]; - } - this.averageFrequencyTimeIncrement /= this.recentFrequencyTimeIncrements.length; - this.frequencyTimeWheelPos += angle; - const FULL_CIRCLE = 360; - if (this.frequencyTimeWheelPos >= FULL_CIRCLE) { - this.frequencyTimeWheelPos = this.frequencyTimeWheelPos % FULL_CIRCLE; - } - } - - this.saveHistory = function() { - this.lastDirection = this.direction; - this.lastVelocity = this.velocity; - this.lastYaw = this.yaw; - this.lastYawDelta = this.yawDelta; - this.lastYawDeltaAcceleration = this.yawDeltaAcceleration; - } -}; // end Motion constructor - -// animation manipulation object -animationOperations = (function() { - - return { - - // helper function for renderMotion(). calculate joint translations based on animation file settings and frequency * time - calculateTranslations: function(animation, ft, direction) { - var jointName = "Hips"; - var joint = animation.joints[jointName]; - var jointTranslations = {x:0, y:0, z:0}; - - // gather modifiers and multipliers - modifiers = new FrequencyMultipliers(joint, direction); - - // calculate translations. Use synthesis filters where specified by the animation data file. - - // sway (oscillation on the x-axis) - if (animation.filters.hasOwnProperty(jointName) && 'swayFilter' in animation.filters[jointName]) { - jointTranslations.x = joint.sway * animation.filters[jointName].swayFilter.calculate - (filter.degToRad(modifiers.swayFrequencyMultiplier * ft + joint.swayPhase)) + joint.swayOffset; - } else { - jointTranslations.x = joint.sway * Math.sin - (filter.degToRad(modifiers.swayFrequencyMultiplier * ft + joint.swayPhase)) + joint.swayOffset; - } - // bob (oscillation on the y-axis) - if (animation.filters.hasOwnProperty(jointName) && 'bobFilter' in animation.filters[jointName]) { - jointTranslations.y = joint.bob * animation.filters[jointName].bobFilter.calculate - (filter.degToRad(modifiers.bobFrequencyMultiplier * ft + joint.bobPhase)) + joint.bobOffset; - } else { - jointTranslations.y = joint.bob * Math.sin - (filter.degToRad(modifiers.bobFrequencyMultiplier * ft + joint.bobPhase)) + joint.bobOffset; - - if (animation.filters.hasOwnProperty(jointName) && 'bobLPFilter' in animation.filters[jointName]) { - jointTranslations.y = filter.clipTrough(jointTranslations.y, joint, 2); - jointTranslations.y = animation.filters[jointName].bobLPFilter.process(jointTranslations.y); - } - } - // thrust (oscillation on the z-axis) - if (animation.filters.hasOwnProperty(jointName) && 'thrustFilter' in animation.filters[jointName]) { - jointTranslations.z = joint.thrust * animation.filters[jointName].thrustFilter.calculate - (filter.degToRad(modifiers.thrustFrequencyMultiplier * ft + joint.thrustPhase)) + joint.thrustOffset; - } else { - jointTranslations.z = joint.thrust * Math.sin - (filter.degToRad(modifiers.thrustFrequencyMultiplier * ft + joint.thrustPhase)) + joint.thrustOffset; - } - return jointTranslations; - }, - - // helper function for renderMotion(). calculate joint rotations based on animation file settings and frequency * time - calculateRotations: function(jointName, animation, ft, direction) { - var joint = animation.joints[jointName]; - var jointRotations = {x:0, y:0, z:0}; - - if (avatar.blenderPreRotations) { - jointRotations = Vec3.sum(jointRotations, walkAssets.blenderPreRotations.joints[jointName]); - } - - // gather frequency multipliers for this joint - modifiers = new FrequencyMultipliers(joint, direction); - - // calculate rotations. Use synthesis filters where specified by the animation data file. - - // calculate pitch - if (animation.filters.hasOwnProperty(jointName) && - 'pitchFilter' in animation.filters[jointName]) { - jointRotations.x += joint.pitch * animation.filters[jointName].pitchFilter.calculate - (filter.degToRad(ft * modifiers.pitchFrequencyMultiplier + joint.pitchPhase)) + joint.pitchOffset; - } else { - jointRotations.x += joint.pitch * Math.sin - (filter.degToRad(ft * modifiers.pitchFrequencyMultiplier + joint.pitchPhase)) + joint.pitchOffset; - } - // calculate yaw - if (animation.filters.hasOwnProperty(jointName) && - 'yawFilter' in animation.filters[jointName]) { - jointRotations.y += joint.yaw * animation.filters[jointName].yawFilter.calculate - (filter.degToRad(ft * modifiers.yawFrequencyMultiplier + joint.yawPhase)) + joint.yawOffset; - } else { - jointRotations.y += joint.yaw * Math.sin - (filter.degToRad(ft * modifiers.yawFrequencyMultiplier + joint.yawPhase)) + joint.yawOffset; - } - // calculate roll - if (animation.filters.hasOwnProperty(jointName) && - 'rollFilter' in animation.filters[jointName]) { - jointRotations.z += joint.roll * animation.filters[jointName].rollFilter.calculate - (filter.degToRad(ft * modifiers.rollFrequencyMultiplier + joint.rollPhase)) + joint.rollOffset; - } else { - jointRotations.z += joint.roll * Math.sin - (filter.degToRad(ft * modifiers.rollFrequencyMultiplier + joint.rollPhase)) + joint.rollOffset; - } - return jointRotations; - }, - - zeroAnimation: function(animation) { - for (i in animation.joints) { - for (j in animation.joints[i]) { - animation.joints[i][j] = 0; - } - } - }, - - blendAnimation: function(sourceAnimation, targetAnimation, percent) { - for (i in targetAnimation.joints) { - targetAnimation.joints[i].pitch += percent * sourceAnimation.joints[i].pitch; - targetAnimation.joints[i].yaw += percent * sourceAnimation.joints[i].yaw; - targetAnimation.joints[i].roll += percent * sourceAnimation.joints[i].roll; - targetAnimation.joints[i].pitchPhase += percent * sourceAnimation.joints[i].pitchPhase; - targetAnimation.joints[i].yawPhase += percent * sourceAnimation.joints[i].yawPhase; - targetAnimation.joints[i].rollPhase += percent * sourceAnimation.joints[i].rollPhase; - targetAnimation.joints[i].pitchOffset += percent * sourceAnimation.joints[i].pitchOffset; - targetAnimation.joints[i].yawOffset += percent * sourceAnimation.joints[i].yawOffset; - targetAnimation.joints[i].rollOffset += percent * sourceAnimation.joints[i].rollOffset; - if (i === "Hips") { - // Hips only - targetAnimation.joints[i].thrust += percent * sourceAnimation.joints[i].thrust; - targetAnimation.joints[i].sway += percent * sourceAnimation.joints[i].sway; - targetAnimation.joints[i].bob += percent * sourceAnimation.joints[i].bob; - targetAnimation.joints[i].thrustPhase += percent * sourceAnimation.joints[i].thrustPhase; - targetAnimation.joints[i].swayPhase += percent * sourceAnimation.joints[i].swayPhase; - targetAnimation.joints[i].bobPhase += percent * sourceAnimation.joints[i].bobPhase; - targetAnimation.joints[i].thrustOffset += percent * sourceAnimation.joints[i].thrustOffset; - targetAnimation.joints[i].swayOffset += percent * sourceAnimation.joints[i].swayOffset; - targetAnimation.joints[i].bobOffset += percent * sourceAnimation.joints[i].bobOffset; - } - } - }, - - deepCopy: function(sourceAnimation, targetAnimation) { - // calibration - targetAnimation.calibration = JSON.parse(JSON.stringify(sourceAnimation.calibration)); - - // harmonics - targetAnimation.harmonics = {}; - if (sourceAnimation.harmonics) { - targetAnimation.harmonics = JSON.parse(JSON.stringify(sourceAnimation.harmonics)); - } - - // filters - targetAnimation.filters = {}; - for (i in sourceAnimation.filters) { - // are any filters specified for this joint? - if (sourceAnimation.filters[i]) { - targetAnimation.filters[i] = sourceAnimation.filters[i]; - // wave shapers - if (sourceAnimation.filters[i].pitchFilter) { - targetAnimation.filters[i].pitchFilter = sourceAnimation.filters[i].pitchFilter; - } - if (sourceAnimation.filters[i].yawFilter) { - targetAnimation.filters[i].yawFilter = sourceAnimation.filters[i].yawFilter; - } - if (sourceAnimation.filters[i].rollFilter) { - targetAnimation.filters[i].rollFilter = sourceAnimation.filters[i].rollFilter; - } - // LP filters - if (sourceAnimation.filters[i].swayLPFilter) { - targetAnimation.filters[i].swayLPFilter = sourceAnimation.filters[i].swayLPFilter; - } - if (sourceAnimation.filters[i].bobLPFilter) { - targetAnimation.filters[i].bobLPFilter = sourceAnimation.filters[i].bobLPFilter; - } - if (sourceAnimation.filters[i].thrustLPFilter) { - targetAnimation.filters[i].thrustLPFilter = sourceAnimation.filters[i].thrustLPFilter; - } - } - } - // joints - targetAnimation.joints = JSON.parse(JSON.stringify(sourceAnimation.joints)); - } - } - -})(); // end animation object literal - -// ReachPose datafile wrapper object -ReachPose = function(reachPoseName) { - this.name = reachPoseName; - this.reachPoseParameters = walkAssets.getReachPoseParameters(reachPoseName); - this.reachPoseDataFile = walkAssets.getReachPoseDataFile(reachPoseName); - this.progress = 0; - this.smoothingFilter = filter.createAveragingFilter(this.reachPoseParameters.smoothing); - this.currentStrength = function() { - // apply optionally smoothed (D)ASDR envelope to reach pose's strength / influence whilst active - var segmentProgress = undefined; // progress through chosen segment - var segmentTimeDelta = undefined; // total change in time over chosen segment - var segmentStrengthDelta = undefined; // total change in strength over chosen segment - var lastStrength = undefined; // the last value the previous segment held - var currentStrength = undefined; // return value - - // select parameters based on segment (a segment being one of (D),A,S,D or R) - if (this.progress >= this.reachPoseParameters.sustain.timing) { - // release segment - segmentProgress = this.progress - this.reachPoseParameters.sustain.timing; - segmentTimeDelta = this.reachPoseParameters.release.timing - this.reachPoseParameters.sustain.timing; - segmentStrengthDelta = this.reachPoseParameters.release.strength - this.reachPoseParameters.sustain.strength; - lastStrength = this.reachPoseParameters.sustain.strength; - } else if (this.progress >= this.reachPoseParameters.decay.timing) { - // sustain phase - segmentProgress = this.progress - this.reachPoseParameters.decay.timing; - segmentTimeDelta = this.reachPoseParameters.sustain.timing - this.reachPoseParameters.decay.timing; - segmentStrengthDelta = this.reachPoseParameters.sustain.strength - this.reachPoseParameters.decay.strength; - lastStrength = this.reachPoseParameters.decay.strength; - } else if (this.progress >= this.reachPoseParameters.attack.timing) { - // decay phase - segmentProgress = this.progress - this.reachPoseParameters.attack.timing; - segmentTimeDelta = this.reachPoseParameters.decay.timing - this.reachPoseParameters.attack.timing; - segmentStrengthDelta = this.reachPoseParameters.decay.strength - this.reachPoseParameters.attack.strength; - lastStrength = this.reachPoseParameters.attack.strength; - } else if (this.progress >= this.reachPoseParameters.delay.timing) { - // attack phase - segmentProgress = this.progress - this.reachPoseParameters.delay.timing; - segmentTimeDelta = this.reachPoseParameters.attack.timing - this.reachPoseParameters.delay.timing; - segmentStrengthDelta = this.reachPoseParameters.attack.strength - this.reachPoseParameters.delay.strength; - lastStrength = 0; //this.delay.strength; - } else { - // delay phase - segmentProgress = this.progress; - segmentTimeDelta = this.reachPoseParameters.delay.timing; - segmentStrengthDelta = this.reachPoseParameters.delay.strength; - lastStrength = 0; - } - currentStrength = segmentTimeDelta > 0 ? lastStrength + segmentStrengthDelta * segmentProgress / segmentTimeDelta - : lastStrength; - // smooth off the response curve - currentStrength = this.smoothingFilter.process(currentStrength); - return currentStrength; - } -}; - -// constructor with default parameters -TransitionParameters = function() { - this.duration = 0.5; - this.easingLower = {x:0.25, y:0.75}; - this.easingUpper = {x:0.75, y:0.25}; - this.reachPoses = []; -} - -const QUARTER_CYCLE = 90; -const HALF_CYCLE = 180; -const THREE_QUARTER_CYCLE = 270; -const FULL_CYCLE = 360; - -// constructor for animation Transition -Transition = function(nextAnimation, lastAnimation, lastTransition, playTransitionReachPoses) { - - if (playTransitionReachPoses === undefined) { - playTransitionReachPoses = true; - } - - // record the current state of animation - this.nextAnimation = nextAnimation; - this.lastAnimation = lastAnimation; - this.lastTransition = lastTransition; - - // collect information about the currently playing animation - this.direction = motion.direction; - this.lastDirection = motion.lastDirection; - this.lastFrequencyTimeWheelPos = motion.frequencyTimeWheelPos; - this.lastFrequencyTimeIncrement = motion.averageFrequencyTimeIncrement; - this.lastFrequencyTimeWheelRadius = motion.frequencyTimeWheelRadius; - this.degreesToTurn = 0; // total degrees to turn the ft wheel before the avatar stops (walk only) - this.degreesRemaining = 0; // remaining degrees to turn the ft wheel before the avatar stops (walk only) - this.lastElapsedFTDegrees = motion.elapsedFTDegrees; // degrees elapsed since last transition start - motion.elapsedFTDegrees = 0; // reset ready for the next transition - motion.frequencyTimeWheelPos = 0; // start the next animation's frequency time wheel from zero - - // set parameters for the transition - this.parameters = new TransitionParameters(); - this.liveReachPoses = []; - if (walkAssets && lastAnimation && nextAnimation) { - // overwrite this.parameters with any transition parameters specified for this particular transition - walkAssets.getTransitionParameters(lastAnimation, nextAnimation, this.parameters); - // fire up any reach poses for this transition - if (playTransitionReachPoses) { - for (poseName in this.parameters.reachPoses) { - this.liveReachPoses.push(new ReachPose(this.parameters.reachPoses[poseName])); - } - } - } - this.startTime = new Date().getTime(); // Starting timestamp (seconds) - this.progress = 0; // how far are we through the transition? - this.filteredProgress = 0; - - // coming to a halt whilst walking? if so, will need a clean stopping point defined - if (motion.isComingToHalt) { - - const FULL_CYCLE_THRESHOLD = 320; - const HALF_CYCLE_THRESHOLD = 140; - const CYCLE_COMMIT_THRESHOLD = 5; - - // how many degrees do we need to turn the walk wheel to finish walking with both feet on the ground? - if (this.lastElapsedFTDegrees < CYCLE_COMMIT_THRESHOLD) { - // just stop the walk cycle right here and blend to idle - this.degreesToTurn = 0; - } else if (this.lastElapsedFTDegrees < HALF_CYCLE) { - // we have not taken a complete step yet, so we advance to the second stop angle - this.degreesToTurn = HALF_CYCLE - this.lastFrequencyTimeWheelPos; - } else if (this.lastFrequencyTimeWheelPos > 0 && this.lastFrequencyTimeWheelPos <= HALF_CYCLE_THRESHOLD) { - // complete the step and stop at 180 - this.degreesToTurn = HALF_CYCLE - this.lastFrequencyTimeWheelPos; - } else if (this.lastFrequencyTimeWheelPos > HALF_CYCLE_THRESHOLD && this.lastFrequencyTimeWheelPos <= HALF_CYCLE) { - // complete the step and next then stop at 0 - this.degreesToTurn = HALF_CYCLE - this.lastFrequencyTimeWheelPos + HALF_CYCLE; - } else if (this.lastFrequencyTimeWheelPos > HALF_CYCLE && this.lastFrequencyTimeWheelPos <= FULL_CYCLE_THRESHOLD) { - // complete the step and stop at 0 - this.degreesToTurn = FULL_CYCLE - this.lastFrequencyTimeWheelPos; - } else { - // complete the step and the next then stop at 180 - this.degreesToTurn = FULL_CYCLE - this.lastFrequencyTimeWheelPos + HALF_CYCLE; - } - - // transition length in this case should be directly proportional to the remaining degrees to turn - var MIN_FT_INCREMENT = 5.0; // degrees per frame - var MIN_TRANSITION_DURATION = 0.4; - const TWO_THIRDS = 0.6667; - this.lastFrequencyTimeIncrement *= TWO_THIRDS; // help ease the transition - var lastFrequencyTimeIncrement = this.lastFrequencyTimeIncrement > MIN_FT_INCREMENT ? - this.lastFrequencyTimeIncrement : MIN_FT_INCREMENT; - var timeToFinish = Math.max(motion.deltaTime * this.degreesToTurn / lastFrequencyTimeIncrement, - MIN_TRANSITION_DURATION); - this.parameters.duration = timeToFinish; - this.degreesRemaining = this.degreesToTurn; - } - - // deal with transition recursion (overlapping transitions) - this.recursionDepth = 0; - this.incrementRecursion = function() { - this.recursionDepth += 1; - - // cancel any continued motion - this.degreesToTurn = 0; - - // limit the number of layered / nested transitions - if (this.lastTransition !== nullTransition) { - this.lastTransition.incrementRecursion(); - if (this.lastTransition.recursionDepth > MAX_TRANSITION_RECURSION) { - this.lastTransition = nullTransition; - } - } - }; - if (this.lastTransition !== nullTransition) { - this.lastTransition.incrementRecursion(); - } - - // end of transition initialisation. begin Transition public methods - - // keep up the pace for the frequency time wheel for the last animation - this.advancePreviousFrequencyTimeWheel = function(deltaTime) { - var wheelAdvance = undefined; - - if (this.lastAnimation === avatar.selectedWalkBlend && - this.nextAnimation === avatar.selectedIdle) { - if (this.degreesRemaining <= 0) { - // stop continued motion - wheelAdvance = 0; - if (motion.isComingToHalt) { - if (this.lastFrequencyTimeWheelPos < QUARTER_CYCLE) { - this.lastFrequencyTimeWheelPos = 0; - } else { - this.lastFrequencyTimeWheelPos = HALF_CYCLE; - } - } - } else { - wheelAdvance = this.lastFrequencyTimeIncrement; - var distanceToTravel = avatar.calibration.strideLength * wheelAdvance / HALF_CYCLE; - if (this.degreesRemaining <= 0) { - distanceToTravel = 0; - this.degreesRemaining = 0; - } else { - this.degreesRemaining -= wheelAdvance; - } - } - } else { - wheelAdvance = this.lastFrequencyTimeIncrement; - } - - // advance the ft wheel - this.lastFrequencyTimeWheelPos += wheelAdvance; - if (this.lastFrequencyTimeWheelPos >= FULL_CYCLE) { - this.lastFrequencyTimeWheelPos = this.lastFrequencyTimeWheelPos % FULL_CYCLE; - } - - // advance ft wheel for the nested (previous) Transition - if (this.lastTransition !== nullTransition) { - this.lastTransition.advancePreviousFrequencyTimeWheel(deltaTime); - } - // update the lastElapsedFTDegrees for short stepping - this.lastElapsedFTDegrees += wheelAdvance; - this.degreesTurned += wheelAdvance; - }; - - this.updateProgress = function() { - const MILLISECONDS_CONVERT = 1000; - const ACCURACY_INCREASER = 1000; - var elapasedTime = (new Date().getTime() - this.startTime) / MILLISECONDS_CONVERT; - this.progress = elapasedTime / this.parameters.duration; - this.progress = Math.round(this.progress * ACCURACY_INCREASER) / ACCURACY_INCREASER; - - // updated nested transition/s - if (this.lastTransition !== nullTransition) { - if (this.lastTransition.updateProgress() === TRANSITION_COMPLETE) { - // the previous transition is now complete - this.lastTransition = nullTransition; - } - } - - // update any reachPoses - for (pose in this.liveReachPoses) { - // use independent timing for reachPoses - this.liveReachPoses[pose].progress += (motion.deltaTime / this.liveReachPoses[pose].reachPoseParameters.duration); - if (this.liveReachPoses[pose].progress >= 1) { - // time to kill off this reach pose - this.liveReachPoses.splice(pose, 1); - } - } - - // update transition progress - this.filteredProgress = filter.bezier(this.progress, this.parameters.easingLower, this.parameters.easingUpper); - return this.progress >= 1 ? TRANSITION_COMPLETE : false; - }; - - this.blendTranslations = function(frequencyTimeWheelPos, direction) { - var lastTranslations = {x:0, y:0, z:0}; - var nextTranslations = animationOperations.calculateTranslations(this.nextAnimation, - frequencyTimeWheelPos, - direction); - // are we blending with a previous, still live transition? - if (this.lastTransition !== nullTransition) { - lastTranslations = this.lastTransition.blendTranslations(this.lastFrequencyTimeWheelPos, - this.lastDirection); - } else { - lastTranslations = animationOperations.calculateTranslations(this.lastAnimation, - this.lastFrequencyTimeWheelPos, - this.lastDirection); - } - - // blend last / next translations - nextTranslations = Vec3.multiply(this.filteredProgress, nextTranslations); - lastTranslations = Vec3.multiply((1 - this.filteredProgress), lastTranslations); - nextTranslations = Vec3.sum(nextTranslations, lastTranslations); - - if (this.liveReachPoses.length > 0) { - for (pose in this.liveReachPoses) { - var reachPoseStrength = this.liveReachPoses[pose].currentStrength(); - var poseTranslations = animationOperations.calculateTranslations( - this.liveReachPoses[pose].reachPoseDataFile, - frequencyTimeWheelPos, - direction); - - // can't use Vec3 operations here, as if x,y or z is zero, the reachPose should have no influence at all - if (Math.abs(poseTranslations.x) > 0) { - nextTranslations.x = reachPoseStrength * poseTranslations.x + (1 - reachPoseStrength) * nextTranslations.x; - } - if (Math.abs(poseTranslations.y) > 0) { - nextTranslations.y = reachPoseStrength * poseTranslations.y + (1 - reachPoseStrength) * nextTranslations.y; - } - if (Math.abs(poseTranslations.z) > 0) { - nextTranslations.z = reachPoseStrength * poseTranslations.z + (1 - reachPoseStrength) * nextTranslations.z; - } - } - } - return nextTranslations; - }; - - this.blendRotations = function(jointName, frequencyTimeWheelPos, direction) { - var lastRotations = {x:0, y:0, z:0}; - var nextRotations = animationOperations.calculateRotations(jointName, - this.nextAnimation, - frequencyTimeWheelPos, - direction); - - // are we blending with a previous, still live transition? - if (this.lastTransition !== nullTransition) { - lastRotations = this.lastTransition.blendRotations(jointName, - this.lastFrequencyTimeWheelPos, - this.lastDirection); - } else { - lastRotations = animationOperations.calculateRotations(jointName, - this.lastAnimation, - this.lastFrequencyTimeWheelPos, - this.lastDirection); - } - // blend last / next translations - nextRotations = Vec3.multiply(this.filteredProgress, nextRotations); - lastRotations = Vec3.multiply((1 - this.filteredProgress), lastRotations); - nextRotations = Vec3.sum(nextRotations, lastRotations); - - // are there reachPoses defined for this transition? - if (this.liveReachPoses.length > 0) { - for (pose in this.liveReachPoses) { - var reachPoseStrength = this.liveReachPoses[pose].currentStrength(); - var poseRotations = animationOperations.calculateRotations(jointName, - this.liveReachPoses[pose].reachPoseDataFile, - frequencyTimeWheelPos, - direction); - - // don't use Vec3 operations here, as if x,y or z is zero, the reach pose should have no influence at all - if (Math.abs(poseRotations.x) > 0) { - nextRotations.x = reachPoseStrength * poseRotations.x + (1 - reachPoseStrength) * nextRotations.x; - } - if (Math.abs(poseRotations.y) > 0) { - nextRotations.y = reachPoseStrength * poseRotations.y + (1 - reachPoseStrength) * nextRotations.y; - } - if (Math.abs(poseRotations.z) > 0) { - nextRotations.z = reachPoseStrength * poseRotations.z + (1 - reachPoseStrength) * nextRotations.z; - } - } - } - return nextRotations; - }; -}; // end Transition constructor - -// individual joint modifiers -FrequencyMultipliers = function(joint, direction) { - // gather multipliers - this.pitchFrequencyMultiplier = 1; - this.yawFrequencyMultiplier = 1; - this.rollFrequencyMultiplier = 1; - this.swayFrequencyMultiplier = 1; - this.bobFrequencyMultiplier = 1; - this.thrustFrequencyMultiplier = 1; - - if (joint) { - if (joint.pitchFrequencyMultiplier) { - this.pitchFrequencyMultiplier = joint.pitchFrequencyMultiplier; - } - if (joint.yawFrequencyMultiplier) { - this.yawFrequencyMultiplier = joint.yawFrequencyMultiplier; - } - if (joint.rollFrequencyMultiplier) { - this.rollFrequencyMultiplier = joint.rollFrequencyMultiplier; - } - if (joint.swayFrequencyMultiplier) { - this.swayFrequencyMultiplier = joint.swayFrequencyMultiplier; - } - if (joint.bobFrequencyMultiplier) { - this.bobFrequencyMultiplier = joint.bobFrequencyMultiplier; - } - if (joint.thrustFrequencyMultiplier) { - this.thrustFrequencyMultiplier = joint.thrustFrequencyMultiplier; - } - } -}; \ No newline at end of file +// +// walkApi.js +// version 1.3 +// +// Created by David Wooldridge, June 2015 +// Copyright © 2014 - 2015 High Fidelity, Inc. +// +// Exposes API for use by walk.js version 1.2+. +// +// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// included here to ensure walkApi.js can be used as an API, separate from walk.js +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; + } + } + // settings + this.headFree = true; + this.armsFree = this.hydraCheck(); // automatically sets true to enable Hydra support - temporary fix + this.makesFootStepSounds = false; + this.blenderPreRotations = false; // temporary fix + this.animationSet = undefined; // currently just one animation set + this.setAnimationSet = function(animationSet) { + this.animationSet = animationSet; + switch (animationSet) { + case 'standardMale': + this.selectedIdle = walkAssets.getAnimationDataFile("MaleIdle"); + this.selectedWalk = walkAssets.getAnimationDataFile("MaleWalk"); + this.selectedWalkBackwards = walkAssets.getAnimationDataFile("MaleWalkBackwards"); + this.selectedSideStepLeft = walkAssets.getAnimationDataFile("MaleSideStepLeft"); + this.selectedSideStepRight = walkAssets.getAnimationDataFile("MaleSideStepRight"); + this.selectedWalkBlend = walkAssets.getAnimationDataFile("WalkBlend"); + this.selectedHover = walkAssets.getAnimationDataFile("MaleHover"); + this.selectedFly = walkAssets.getAnimationDataFile("MaleFly"); + this.selectedFlyBackwards = walkAssets.getAnimationDataFile("MaleFlyBackwards"); + this.selectedFlyDown = walkAssets.getAnimationDataFile("MaleFlyDown"); + this.selectedFlyUp = walkAssets.getAnimationDataFile("MaleFlyUp"); + this.selectedFlyBlend = walkAssets.getAnimationDataFile("FlyBlend"); + this.currentAnimation = this.selectedIdle; + return; + } + } + this.setAnimationSet('standardMale'); + + // calibration + this.calibration = { + hipsToFeet: 1, + strideLength: this.selectedWalk.calibration.strideLength + } + this.distanceFromSurface = 0; + this.calibrate = function() { + // Triple check: measurements are taken three times to ensure accuracy - the first result is often too large + const MAX_ATTEMPTS = 3; + var attempts = MAX_ATTEMPTS; + var extraAttempts = 0; + do { + for (joint in walkAssets.animationReference.joints) { + var IKChain = walkAssets.animationReference.joints[joint].IKChain; + + // only need to zero right leg IK chain and hips + if (IKChain === "RightLeg" || joint === "Hips" ) { + MyAvatar.setJointRotation(joint, Quat.fromPitchYawRollDegrees(0, 0, 0)); + } + } + this.calibration.hipsToFeet = MyAvatar.getJointPosition("Hips").y - MyAvatar.getJointPosition("RightToeBase").y; + + // maybe measuring before Blender pre-rotations have been applied? + if (this.calibration.hipsToFeet < 0 && this.blenderPreRotations) { + this.calibration.hipsToFeet *= -1; + } + + if (this.calibration.hipsToFeet === 0 && extraAttempts < 100) { + attempts++; + extraAttempts++;// Interface can sometimes report zero for hips to feet. if so, we try again. + } + } while (attempts-- > 1) + + // just in case + if (this.calibration.hipsToFeet <= 0 || isNaN(this.calibration.hipsToFeet)) { + this.calibration.hipsToFeet = 1; + print('walk.js error: Unable to get a non-zero measurement for the avatar hips to feet measure. Hips to feet set to default value ('+ + this.calibration.hipsToFeet.toFixed(3)+'m). This will cause some foot sliding. If your avatar has only just appeared, it is recommended that you re-load the walk script.'); + } else { + print('walk.js info: Hips to feet calibrated to '+this.calibration.hipsToFeet.toFixed(3)+'m'); + } + } + + // pose the fingers + this.poseFingers = function() { + for (knuckle in walkAssets.animationReference.leftHand) { + if (walkAssets.animationReference.leftHand[knuckle].IKChain === "LeftHandThumb") { + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, -4)); + } else { + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, 5)); + } + } + for (knuckle in walkAssets.animationReference.rightHand) { + if (walkAssets.animationReference.rightHand[knuckle].IKChain === "RightHandThumb") { + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(0, 0, 4)); + } else { + MyAvatar.setJointRotation(knuckle, Quat.fromPitchYawRollDegrees(16, 0, -5)); + } + } + }; + this.calibrate(); + this.poseFingers(); + + // footsteps + this.nextStep = RIGHT; // the first step is right, because the waveforms say so + this.leftAudioInjector = null; + this.rightAudioInjector = null; + this.makeFootStepSound = function() { + // correlate footstep volume with avatar speed. place the audio source at the feet, not the hips + const SPEED_THRESHOLD = 0.4; + const VOLUME_ATTENUATION = 0.8; + const MIN_VOLUME = 0.5; + var volume = Vec3.length(motion.velocity) > SPEED_THRESHOLD ? + VOLUME_ATTENUATION * Vec3.length(motion.velocity) / MAX_WALK_SPEED : MIN_VOLUME; + volume = volume > 1 ? 1 : volume; // occurs when landing at speed - can walk faster than max walking speed + var options = { + position: Vec3.sum(MyAvatar.position, {x:0, y: -this.calibration.hipsToFeet, z:0}), + volume: volume + }; + if (this.nextStep === RIGHT) { + if (this.rightAudioInjector === null) { + this.rightAudioInjector = Audio.playSound(walkAssets.footsteps[0], options); + } else { + this.rightAudioInjector.setOptions(options); + this.rightAudioInjector.restart(); + } + this.nextStep = LEFT; + } else if (this.nextStep === LEFT) { + if (this.leftAudioInjector === null) { + this.leftAudioInjector = Audio.playSound(walkAssets.footsteps[1], options); + } else { + this.leftAudioInjector.setOptions(options); + this.leftAudioInjector.restart(); + } + this.nextStep = RIGHT; + } + } +}; + +// constructor for the Motion object +Motion = function() { + this.isLive = true; + // locomotion status + this.state = STATIC; + this.nextState = STATIC; + this.isMoving = false; + this.isWalkingSpeed = false; + this.isFlyingSpeed = false; + this.isAccelerating = false; + this.isDecelerating = false; + this.isDeceleratingFast = false; + this.isComingToHalt = false; + this.directedAcceleration = 0; + + // used to make sure at least one step has been taken when transitioning from a walk cycle + this.elapsedFTDegrees = 0; + + // the current transition (any layered transitions are nested within this transition) + this.currentTransition = null; + + // orientation, locomotion and timing + this.velocity = {x:0, y:0, z:0}; + this.acceleration = {x:0, y:0, z:0}; + this.yaw = Quat.safeEulerAngles(MyAvatar.orientation).y; + this.yawDelta = 0; + this.yawDeltaAcceleration = 0; + this.direction = FORWARDS; + this.deltaTime = 0; + + // historical orientation, locomotion and timing + this.lastDirection = FORWARDS; + this.lastVelocity = {x:0, y:0, z:0}; + this.lastYaw = Quat.safeEulerAngles(MyAvatar.orientation).y; + this.lastYawDelta = 0; + this.lastYawDeltaAcceleration = 0; + + // Quat.safeEulerAngles(MyAvatar.orientation).y tends to repeat values between frames, so values are filtered + var YAW_SMOOTHING = 22; + this.yawFilter = filter.createAveragingFilter(YAW_SMOOTHING); + this.deltaTimeFilter = filter.createAveragingFilter(YAW_SMOOTHING); + this.yawDeltaAccelerationFilter = filter.createAveragingFilter(YAW_SMOOTHING); + + // assess locomotion state + this.assess = function(deltaTime) { + // calculate avatar frame speed, velocity and acceleration + this.deltaTime = deltaTime; + this.velocity = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), MyAvatar.getVelocity()); + var lateralVelocity = Math.sqrt(Math.pow(this.velocity.x, 2) + Math.pow(this.velocity.z, 2)); + + // MyAvatar.getAcceleration() currently not working. bug report submitted: https://worklist.net/20527 + var acceleration = {x:0, y:0, z:0}; + this.acceleration.x = (this.velocity.x - this.lastVelocity.x) / deltaTime; + this.acceleration.y = (this.velocity.y - this.lastVelocity.y) / deltaTime; + this.acceleration.z = (this.velocity.z - this.lastVelocity.z) / deltaTime; + + // MyAvatar.getAngularVelocity and MyAvatar.getAngularAcceleration currently not working. bug report submitted + this.yaw = Quat.safeEulerAngles(MyAvatar.orientation).y; + if (this.lastYaw < 0 && this.yaw > 0 || this.lastYaw > 0 && this.yaw < 0) { + this.lastYaw *= -1; + } + var timeDelta = this.deltaTimeFilter.process(deltaTime); + this.yawDelta = filter.degToRad(this.yawFilter.process(this.lastYaw - this.yaw)) / timeDelta; + this.yawDeltaAcceleration = this.yawDeltaAccelerationFilter.process(this.lastYawDelta - this.yawDelta) / timeDelta; + + // how far above the surface is the avatar? (for testing / validation purposes) + var pickRay = {origin: MyAvatar.position, direction: {x:0, y:-1, z:0}}; + var distanceFromSurface = Entities.findRayIntersectionBlocking(pickRay).distance; + avatar.distanceFromSurface = distanceFromSurface - avatar.calibration.hipsToFeet; + + // determine principle direction of locomotion + var FWD_BACK_BIAS = 100; // helps prevent false sidestep condition detection when banking hard + if (Math.abs(this.velocity.x) > Math.abs(this.velocity.y) && + Math.abs(this.velocity.x) > FWD_BACK_BIAS * Math.abs(this.velocity.z)) { + if (this.velocity.x < 0) { + this.directedAcceleration = -this.acceleration.x; + this.direction = LEFT; + } else if (this.velocity.x > 0){ + this.directedAcceleration = this.acceleration.x; + this.direction = RIGHT; + } + } else if (Math.abs(this.velocity.y) > Math.abs(this.velocity.x) && + Math.abs(this.velocity.y) > Math.abs(this.velocity.z)) { + if (this.velocity.y > 0) { + this.directedAcceleration = this.acceleration.y; + this.direction = UP; + } else if (this.velocity.y < 0) { + this.directedAcceleration = -this.acceleration.y; + this.direction = DOWN; + } + } else if (FWD_BACK_BIAS * Math.abs(this.velocity.z) > Math.abs(this.velocity.x) && + Math.abs(this.velocity.z) > Math.abs(this.velocity.y)) { + if (this.velocity.z < 0) { + this.direction = FORWARDS; + this.directedAcceleration = -this.acceleration.z; + } else if (this.velocity.z > 0) { + this.directedAcceleration = this.acceleration.z; + this.direction = BACKWARDS; + } + } else { + this.direction = NONE; + this.directedAcceleration = 0; + } + + // set speed flags + if (Vec3.length(this.velocity) < MOVE_THRESHOLD) { + this.isMoving = false; + this.isWalkingSpeed = false; + this.isFlyingSpeed = false; + this.isComingToHalt = false; + } else if (Vec3.length(this.velocity) < MAX_WALK_SPEED) { + this.isMoving = true; + this.isWalkingSpeed = true; + this.isFlyingSpeed = false; + } else { + this.isMoving = true; + this.isWalkingSpeed = false; + this.isFlyingSpeed = true; + } + + // set acceleration flags + if (this.directedAcceleration > ACCELERATION_THRESHOLD) { + this.isAccelerating = true; + this.isDecelerating = false; + this.isDeceleratingFast = false; + this.isComingToHalt = false; + } else if (this.directedAcceleration < DECELERATION_THRESHOLD) { + this.isAccelerating = false; + this.isDecelerating = true; + this.isDeceleratingFast = (this.directedAcceleration < FAST_DECELERATION_THRESHOLD); + } else { + this.isAccelerating = false; + this.isDecelerating = false; + this.isDeceleratingFast = false; + } + + // use the gathered information to build up some spatial awareness + var isOnSurface = (avatar.distanceFromSurface < ON_SURFACE_THRESHOLD); + var isUnderGravity = (avatar.distanceFromSurface < GRAVITY_THRESHOLD); + var isTakingOff = (isUnderGravity && this.velocity.y > OVERCOME_GRAVITY_SPEED); + var isComingInToLand = (isUnderGravity && this.velocity.y < -OVERCOME_GRAVITY_SPEED); + var aboutToLand = isComingInToLand && avatar.distanceFromSurface < LANDING_THRESHOLD; + var surfaceMotion = isOnSurface && this.isMoving; + var acceleratingAndAirborne = this.isAccelerating && !isOnSurface; + var goingTooFastToWalk = !this.isDecelerating && this.isFlyingSpeed; + var movingDirectlyUpOrDown = (this.direction === UP || this.direction === DOWN) + var maybeBouncing = Math.abs(this.acceleration.y > BOUNCE_ACCELERATION_THRESHOLD) ? true : false; + + // we now have enough information to set the appropriate locomotion mode + switch (this.state) { + case STATIC: + var staticToAirMotion = this.isMoving && (acceleratingAndAirborne || goingTooFastToWalk || + (movingDirectlyUpOrDown && !isOnSurface)); + var staticToSurfaceMotion = surfaceMotion && !motion.isComingToHalt && !movingDirectlyUpOrDown && + !this.isDecelerating && lateralVelocity > MOVE_THRESHOLD; + + if (staticToAirMotion) { + this.nextState = AIR_MOTION; + } else if (staticToSurfaceMotion) { + this.nextState = SURFACE_MOTION; + } else { + this.nextState = STATIC; + } + break; + + case SURFACE_MOTION: + var surfaceMotionToStatic = !this.isMoving || + (this.isDecelerating && motion.lastDirection !== DOWN && surfaceMotion && + !maybeBouncing && Vec3.length(this.velocity) < MAX_WALK_SPEED); + var surfaceMotionToAirMotion = (acceleratingAndAirborne || goingTooFastToWalk || movingDirectlyUpOrDown) && + (!surfaceMotion && isTakingOff) || + (!surfaceMotion && this.isMoving && !isComingInToLand); + + if (surfaceMotionToStatic) { + // working on the assumption that stopping is now inevitable + if (!motion.isComingToHalt && isOnSurface) { + motion.isComingToHalt = true; + } + this.nextState = STATIC; + } else if (surfaceMotionToAirMotion) { + this.nextState = AIR_MOTION; + } else { + this.nextState = SURFACE_MOTION; + } + break; + + case AIR_MOTION: + var airMotionToSurfaceMotion = (surfaceMotion || aboutToLand) && !movingDirectlyUpOrDown; + var airMotionToStatic = !this.isMoving && this.direction === this.lastDirection; + + if (airMotionToSurfaceMotion){ + this.nextState = SURFACE_MOTION; + } else if (airMotionToStatic) { + this.nextState = STATIC; + } else { + this.nextState = AIR_MOTION; + } + break; + } + } + + // frequency time wheel (foot / ground speed matching) + const DEFAULT_HIPS_TO_FEET = 1; + this.frequencyTimeWheelPos = 0; + this.frequencyTimeWheelRadius = DEFAULT_HIPS_TO_FEET / 2; + this.recentFrequencyTimeIncrements = []; + const FT_WHEEL_HISTORY_LENGTH = 8; + for (var i = 0; i < FT_WHEEL_HISTORY_LENGTH; i++) { + this.recentFrequencyTimeIncrements.push(0); + } + this.averageFrequencyTimeIncrement = 0; + + this.advanceFrequencyTimeWheel = function(angle){ + this.elapsedFTDegrees += angle; + // keep a running average of increments for use in transitions (used during transitioning) + this.recentFrequencyTimeIncrements.push(angle); + this.recentFrequencyTimeIncrements.shift(); + for (increment in this.recentFrequencyTimeIncrements) { + this.averageFrequencyTimeIncrement += this.recentFrequencyTimeIncrements[increment]; + } + this.averageFrequencyTimeIncrement /= this.recentFrequencyTimeIncrements.length; + this.frequencyTimeWheelPos += angle; + const FULL_CIRCLE = 360; + if (this.frequencyTimeWheelPos >= FULL_CIRCLE) { + this.frequencyTimeWheelPos = this.frequencyTimeWheelPos % FULL_CIRCLE; + } + } + + this.saveHistory = function() { + this.lastDirection = this.direction; + this.lastVelocity = this.velocity; + this.lastYaw = this.yaw; + this.lastYawDelta = this.yawDelta; + this.lastYawDeltaAcceleration = this.yawDeltaAcceleration; + } +}; // end Motion constructor + +// animation manipulation object +animationOperations = (function() { + + return { + + // helper function for renderMotion(). calculate joint translations based on animation file settings and frequency * time + calculateTranslations: function(animation, ft, direction) { + var jointName = "Hips"; + var joint = animation.joints[jointName]; + var jointTranslations = {x:0, y:0, z:0}; + + // gather modifiers and multipliers + modifiers = new FrequencyMultipliers(joint, direction); + + // calculate translations. Use synthesis filters where specified by the animation data file. + + // sway (oscillation on the x-axis) + if (animation.filters.hasOwnProperty(jointName) && 'swayFilter' in animation.filters[jointName]) { + jointTranslations.x = joint.sway * animation.filters[jointName].swayFilter.calculate + (filter.degToRad(modifiers.swayFrequencyMultiplier * ft + joint.swayPhase)) + joint.swayOffset; + } else { + jointTranslations.x = joint.sway * Math.sin + (filter.degToRad(modifiers.swayFrequencyMultiplier * ft + joint.swayPhase)) + joint.swayOffset; + } + // bob (oscillation on the y-axis) + if (animation.filters.hasOwnProperty(jointName) && 'bobFilter' in animation.filters[jointName]) { + jointTranslations.y = joint.bob * animation.filters[jointName].bobFilter.calculate + (filter.degToRad(modifiers.bobFrequencyMultiplier * ft + joint.bobPhase)) + joint.bobOffset; + } else { + jointTranslations.y = joint.bob * Math.sin + (filter.degToRad(modifiers.bobFrequencyMultiplier * ft + joint.bobPhase)) + joint.bobOffset; + + if (animation.filters.hasOwnProperty(jointName) && 'bobLPFilter' in animation.filters[jointName]) { + jointTranslations.y = filter.clipTrough(jointTranslations.y, joint, 2); + jointTranslations.y = animation.filters[jointName].bobLPFilter.process(jointTranslations.y); + } + } + // thrust (oscillation on the z-axis) + if (animation.filters.hasOwnProperty(jointName) && 'thrustFilter' in animation.filters[jointName]) { + jointTranslations.z = joint.thrust * animation.filters[jointName].thrustFilter.calculate + (filter.degToRad(modifiers.thrustFrequencyMultiplier * ft + joint.thrustPhase)) + joint.thrustOffset; + } else { + jointTranslations.z = joint.thrust * Math.sin + (filter.degToRad(modifiers.thrustFrequencyMultiplier * ft + joint.thrustPhase)) + joint.thrustOffset; + } + return jointTranslations; + }, + + // helper function for renderMotion(). calculate joint rotations based on animation file settings and frequency * time + calculateRotations: function(jointName, animation, ft, direction) { + var joint = animation.joints[jointName]; + var jointRotations = {x:0, y:0, z:0}; + + if (avatar.blenderPreRotations) { + jointRotations = Vec3.sum(jointRotations, walkAssets.blenderPreRotations.joints[jointName]); + } + + // gather frequency multipliers for this joint + modifiers = new FrequencyMultipliers(joint, direction); + + // calculate rotations. Use synthesis filters where specified by the animation data file. + + // calculate pitch + if (animation.filters.hasOwnProperty(jointName) && + 'pitchFilter' in animation.filters[jointName]) { + jointRotations.x += joint.pitch * animation.filters[jointName].pitchFilter.calculate + (filter.degToRad(ft * modifiers.pitchFrequencyMultiplier + joint.pitchPhase)) + joint.pitchOffset; + } else { + jointRotations.x += joint.pitch * Math.sin + (filter.degToRad(ft * modifiers.pitchFrequencyMultiplier + joint.pitchPhase)) + joint.pitchOffset; + } + // calculate yaw + if (animation.filters.hasOwnProperty(jointName) && + 'yawFilter' in animation.filters[jointName]) { + jointRotations.y += joint.yaw * animation.filters[jointName].yawFilter.calculate + (filter.degToRad(ft * modifiers.yawFrequencyMultiplier + joint.yawPhase)) + joint.yawOffset; + } else { + jointRotations.y += joint.yaw * Math.sin + (filter.degToRad(ft * modifiers.yawFrequencyMultiplier + joint.yawPhase)) + joint.yawOffset; + } + // calculate roll + if (animation.filters.hasOwnProperty(jointName) && + 'rollFilter' in animation.filters[jointName]) { + jointRotations.z += joint.roll * animation.filters[jointName].rollFilter.calculate + (filter.degToRad(ft * modifiers.rollFrequencyMultiplier + joint.rollPhase)) + joint.rollOffset; + } else { + jointRotations.z += joint.roll * Math.sin + (filter.degToRad(ft * modifiers.rollFrequencyMultiplier + joint.rollPhase)) + joint.rollOffset; + } + return jointRotations; + }, + + zeroAnimation: function(animation) { + for (i in animation.joints) { + for (j in animation.joints[i]) { + animation.joints[i][j] = 0; + } + } + }, + + blendAnimation: function(sourceAnimation, targetAnimation, percent) { + for (i in targetAnimation.joints) { + targetAnimation.joints[i].pitch += percent * sourceAnimation.joints[i].pitch; + targetAnimation.joints[i].yaw += percent * sourceAnimation.joints[i].yaw; + targetAnimation.joints[i].roll += percent * sourceAnimation.joints[i].roll; + targetAnimation.joints[i].pitchPhase += percent * sourceAnimation.joints[i].pitchPhase; + targetAnimation.joints[i].yawPhase += percent * sourceAnimation.joints[i].yawPhase; + targetAnimation.joints[i].rollPhase += percent * sourceAnimation.joints[i].rollPhase; + targetAnimation.joints[i].pitchOffset += percent * sourceAnimation.joints[i].pitchOffset; + targetAnimation.joints[i].yawOffset += percent * sourceAnimation.joints[i].yawOffset; + targetAnimation.joints[i].rollOffset += percent * sourceAnimation.joints[i].rollOffset; + if (i === "Hips") { + // Hips only + targetAnimation.joints[i].thrust += percent * sourceAnimation.joints[i].thrust; + targetAnimation.joints[i].sway += percent * sourceAnimation.joints[i].sway; + targetAnimation.joints[i].bob += percent * sourceAnimation.joints[i].bob; + targetAnimation.joints[i].thrustPhase += percent * sourceAnimation.joints[i].thrustPhase; + targetAnimation.joints[i].swayPhase += percent * sourceAnimation.joints[i].swayPhase; + targetAnimation.joints[i].bobPhase += percent * sourceAnimation.joints[i].bobPhase; + targetAnimation.joints[i].thrustOffset += percent * sourceAnimation.joints[i].thrustOffset; + targetAnimation.joints[i].swayOffset += percent * sourceAnimation.joints[i].swayOffset; + targetAnimation.joints[i].bobOffset += percent * sourceAnimation.joints[i].bobOffset; + } + } + }, + + deepCopy: function(sourceAnimation, targetAnimation) { + // calibration + targetAnimation.calibration = JSON.parse(JSON.stringify(sourceAnimation.calibration)); + + // harmonics + targetAnimation.harmonics = {}; + if (sourceAnimation.harmonics) { + targetAnimation.harmonics = JSON.parse(JSON.stringify(sourceAnimation.harmonics)); + } + + // filters + targetAnimation.filters = {}; + for (i in sourceAnimation.filters) { + // are any filters specified for this joint? + if (sourceAnimation.filters[i]) { + targetAnimation.filters[i] = sourceAnimation.filters[i]; + // wave shapers + if (sourceAnimation.filters[i].pitchFilter) { + targetAnimation.filters[i].pitchFilter = sourceAnimation.filters[i].pitchFilter; + } + if (sourceAnimation.filters[i].yawFilter) { + targetAnimation.filters[i].yawFilter = sourceAnimation.filters[i].yawFilter; + } + if (sourceAnimation.filters[i].rollFilter) { + targetAnimation.filters[i].rollFilter = sourceAnimation.filters[i].rollFilter; + } + // LP filters + if (sourceAnimation.filters[i].swayLPFilter) { + targetAnimation.filters[i].swayLPFilter = sourceAnimation.filters[i].swayLPFilter; + } + if (sourceAnimation.filters[i].bobLPFilter) { + targetAnimation.filters[i].bobLPFilter = sourceAnimation.filters[i].bobLPFilter; + } + if (sourceAnimation.filters[i].thrustLPFilter) { + targetAnimation.filters[i].thrustLPFilter = sourceAnimation.filters[i].thrustLPFilter; + } + } + } + // joints + targetAnimation.joints = JSON.parse(JSON.stringify(sourceAnimation.joints)); + } + } + +})(); // end animation object literal + +// ReachPose datafile wrapper object +ReachPose = function(reachPoseName) { + this.name = reachPoseName; + this.reachPoseParameters = walkAssets.getReachPoseParameters(reachPoseName); + this.reachPoseDataFile = walkAssets.getReachPoseDataFile(reachPoseName); + this.progress = 0; + this.smoothingFilter = filter.createAveragingFilter(this.reachPoseParameters.smoothing); + this.currentStrength = function() { + // apply optionally smoothed (D)ASDR envelope to reach pose's strength / influence whilst active + var segmentProgress = undefined; // progress through chosen segment + var segmentTimeDelta = undefined; // total change in time over chosen segment + var segmentStrengthDelta = undefined; // total change in strength over chosen segment + var lastStrength = undefined; // the last value the previous segment held + var currentStrength = undefined; // return value + + // select parameters based on segment (a segment being one of (D),A,S,D or R) + if (this.progress >= this.reachPoseParameters.sustain.timing) { + // release segment + segmentProgress = this.progress - this.reachPoseParameters.sustain.timing; + segmentTimeDelta = this.reachPoseParameters.release.timing - this.reachPoseParameters.sustain.timing; + segmentStrengthDelta = this.reachPoseParameters.release.strength - this.reachPoseParameters.sustain.strength; + lastStrength = this.reachPoseParameters.sustain.strength; + } else if (this.progress >= this.reachPoseParameters.decay.timing) { + // sustain phase + segmentProgress = this.progress - this.reachPoseParameters.decay.timing; + segmentTimeDelta = this.reachPoseParameters.sustain.timing - this.reachPoseParameters.decay.timing; + segmentStrengthDelta = this.reachPoseParameters.sustain.strength - this.reachPoseParameters.decay.strength; + lastStrength = this.reachPoseParameters.decay.strength; + } else if (this.progress >= this.reachPoseParameters.attack.timing) { + // decay phase + segmentProgress = this.progress - this.reachPoseParameters.attack.timing; + segmentTimeDelta = this.reachPoseParameters.decay.timing - this.reachPoseParameters.attack.timing; + segmentStrengthDelta = this.reachPoseParameters.decay.strength - this.reachPoseParameters.attack.strength; + lastStrength = this.reachPoseParameters.attack.strength; + } else if (this.progress >= this.reachPoseParameters.delay.timing) { + // attack phase + segmentProgress = this.progress - this.reachPoseParameters.delay.timing; + segmentTimeDelta = this.reachPoseParameters.attack.timing - this.reachPoseParameters.delay.timing; + segmentStrengthDelta = this.reachPoseParameters.attack.strength - this.reachPoseParameters.delay.strength; + lastStrength = 0; //this.delay.strength; + } else { + // delay phase + segmentProgress = this.progress; + segmentTimeDelta = this.reachPoseParameters.delay.timing; + segmentStrengthDelta = this.reachPoseParameters.delay.strength; + lastStrength = 0; + } + currentStrength = segmentTimeDelta > 0 ? lastStrength + segmentStrengthDelta * segmentProgress / segmentTimeDelta + : lastStrength; + // smooth off the response curve + currentStrength = this.smoothingFilter.process(currentStrength); + return currentStrength; + } +}; + +// constructor with default parameters +TransitionParameters = function() { + this.duration = 0.5; + this.easingLower = {x:0.25, y:0.75}; + this.easingUpper = {x:0.75, y:0.25}; + this.reachPoses = []; +} + +const QUARTER_CYCLE = 90; +const HALF_CYCLE = 180; +const THREE_QUARTER_CYCLE = 270; +const FULL_CYCLE = 360; + +// constructor for animation Transition +Transition = function(nextAnimation, lastAnimation, lastTransition, playTransitionReachPoses) { + + if (playTransitionReachPoses === undefined) { + playTransitionReachPoses = true; + } + + // record the current state of animation + this.nextAnimation = nextAnimation; + this.lastAnimation = lastAnimation; + this.lastTransition = lastTransition; + + // collect information about the currently playing animation + this.direction = motion.direction; + this.lastDirection = motion.lastDirection; + this.lastFrequencyTimeWheelPos = motion.frequencyTimeWheelPos; + this.lastFrequencyTimeIncrement = motion.averageFrequencyTimeIncrement; + this.lastFrequencyTimeWheelRadius = motion.frequencyTimeWheelRadius; + this.degreesToTurn = 0; // total degrees to turn the ft wheel before the avatar stops (walk only) + this.degreesRemaining = 0; // remaining degrees to turn the ft wheel before the avatar stops (walk only) + this.lastElapsedFTDegrees = motion.elapsedFTDegrees; // degrees elapsed since last transition start + motion.elapsedFTDegrees = 0; // reset ready for the next transition + motion.frequencyTimeWheelPos = 0; // start the next animation's frequency time wheel from zero + + // set parameters for the transition + this.parameters = new TransitionParameters(); + this.liveReachPoses = []; + if (walkAssets && lastAnimation && nextAnimation) { + // overwrite this.parameters with any transition parameters specified for this particular transition + walkAssets.getTransitionParameters(lastAnimation, nextAnimation, this.parameters); + // fire up any reach poses for this transition + if (playTransitionReachPoses) { + for (poseName in this.parameters.reachPoses) { + this.liveReachPoses.push(new ReachPose(this.parameters.reachPoses[poseName])); + } + } + } + this.startTime = new Date().getTime(); // Starting timestamp (seconds) + this.progress = 0; // how far are we through the transition? + this.filteredProgress = 0; + + // coming to a halt whilst walking? if so, will need a clean stopping point defined + if (motion.isComingToHalt) { + + const FULL_CYCLE_THRESHOLD = 320; + const HALF_CYCLE_THRESHOLD = 140; + const CYCLE_COMMIT_THRESHOLD = 5; + + // how many degrees do we need to turn the walk wheel to finish walking with both feet on the ground? + if (this.lastElapsedFTDegrees < CYCLE_COMMIT_THRESHOLD) { + // just stop the walk cycle right here and blend to idle + this.degreesToTurn = 0; + } else if (this.lastElapsedFTDegrees < HALF_CYCLE) { + // we have not taken a complete step yet, so we advance to the second stop angle + this.degreesToTurn = HALF_CYCLE - this.lastFrequencyTimeWheelPos; + } else if (this.lastFrequencyTimeWheelPos > 0 && this.lastFrequencyTimeWheelPos <= HALF_CYCLE_THRESHOLD) { + // complete the step and stop at 180 + this.degreesToTurn = HALF_CYCLE - this.lastFrequencyTimeWheelPos; + } else if (this.lastFrequencyTimeWheelPos > HALF_CYCLE_THRESHOLD && this.lastFrequencyTimeWheelPos <= HALF_CYCLE) { + // complete the step and next then stop at 0 + this.degreesToTurn = HALF_CYCLE - this.lastFrequencyTimeWheelPos + HALF_CYCLE; + } else if (this.lastFrequencyTimeWheelPos > HALF_CYCLE && this.lastFrequencyTimeWheelPos <= FULL_CYCLE_THRESHOLD) { + // complete the step and stop at 0 + this.degreesToTurn = FULL_CYCLE - this.lastFrequencyTimeWheelPos; + } else { + // complete the step and the next then stop at 180 + this.degreesToTurn = FULL_CYCLE - this.lastFrequencyTimeWheelPos + HALF_CYCLE; + } + + // transition length in this case should be directly proportional to the remaining degrees to turn + var MIN_FT_INCREMENT = 5.0; // degrees per frame + var MIN_TRANSITION_DURATION = 0.4; + const TWO_THIRDS = 0.6667; + this.lastFrequencyTimeIncrement *= TWO_THIRDS; // help ease the transition + var lastFrequencyTimeIncrement = this.lastFrequencyTimeIncrement > MIN_FT_INCREMENT ? + this.lastFrequencyTimeIncrement : MIN_FT_INCREMENT; + var timeToFinish = Math.max(motion.deltaTime * this.degreesToTurn / lastFrequencyTimeIncrement, + MIN_TRANSITION_DURATION); + this.parameters.duration = timeToFinish; + this.degreesRemaining = this.degreesToTurn; + } + + // deal with transition recursion (overlapping transitions) + this.recursionDepth = 0; + this.incrementRecursion = function() { + this.recursionDepth += 1; + + // cancel any continued motion + this.degreesToTurn = 0; + + // limit the number of layered / nested transitions + if (this.lastTransition !== nullTransition) { + this.lastTransition.incrementRecursion(); + if (this.lastTransition.recursionDepth > MAX_TRANSITION_RECURSION) { + this.lastTransition = nullTransition; + } + } + }; + if (this.lastTransition !== nullTransition) { + this.lastTransition.incrementRecursion(); + } + + // end of transition initialisation. begin Transition public methods + + // keep up the pace for the frequency time wheel for the last animation + this.advancePreviousFrequencyTimeWheel = function(deltaTime) { + var wheelAdvance = undefined; + + if (this.lastAnimation === avatar.selectedWalkBlend && + this.nextAnimation === avatar.selectedIdle) { + if (this.degreesRemaining <= 0) { + // stop continued motion + wheelAdvance = 0; + if (motion.isComingToHalt) { + if (this.lastFrequencyTimeWheelPos < QUARTER_CYCLE) { + this.lastFrequencyTimeWheelPos = 0; + } else { + this.lastFrequencyTimeWheelPos = HALF_CYCLE; + } + } + } else { + wheelAdvance = this.lastFrequencyTimeIncrement; + var distanceToTravel = avatar.calibration.strideLength * wheelAdvance / HALF_CYCLE; + if (this.degreesRemaining <= 0) { + distanceToTravel = 0; + this.degreesRemaining = 0; + } else { + this.degreesRemaining -= wheelAdvance; + } + } + } else { + wheelAdvance = this.lastFrequencyTimeIncrement; + } + + // advance the ft wheel + this.lastFrequencyTimeWheelPos += wheelAdvance; + if (this.lastFrequencyTimeWheelPos >= FULL_CYCLE) { + this.lastFrequencyTimeWheelPos = this.lastFrequencyTimeWheelPos % FULL_CYCLE; + } + + // advance ft wheel for the nested (previous) Transition + if (this.lastTransition !== nullTransition) { + this.lastTransition.advancePreviousFrequencyTimeWheel(deltaTime); + } + // update the lastElapsedFTDegrees for short stepping + this.lastElapsedFTDegrees += wheelAdvance; + this.degreesTurned += wheelAdvance; + }; + + this.updateProgress = function() { + const MILLISECONDS_CONVERT = 1000; + const ACCURACY_INCREASER = 1000; + var elapasedTime = (new Date().getTime() - this.startTime) / MILLISECONDS_CONVERT; + this.progress = elapasedTime / this.parameters.duration; + this.progress = Math.round(this.progress * ACCURACY_INCREASER) / ACCURACY_INCREASER; + + // updated nested transition/s + if (this.lastTransition !== nullTransition) { + if (this.lastTransition.updateProgress() === TRANSITION_COMPLETE) { + // the previous transition is now complete + this.lastTransition = nullTransition; + } + } + + // update any reachPoses + for (pose in this.liveReachPoses) { + // use independent timing for reachPoses + this.liveReachPoses[pose].progress += (motion.deltaTime / this.liveReachPoses[pose].reachPoseParameters.duration); + if (this.liveReachPoses[pose].progress >= 1) { + // time to kill off this reach pose + this.liveReachPoses.splice(pose, 1); + } + } + + // update transition progress + this.filteredProgress = filter.bezier(this.progress, this.parameters.easingLower, this.parameters.easingUpper); + return this.progress >= 1 ? TRANSITION_COMPLETE : false; + }; + + this.blendTranslations = function(frequencyTimeWheelPos, direction) { + var lastTranslations = {x:0, y:0, z:0}; + var nextTranslations = animationOperations.calculateTranslations(this.nextAnimation, + frequencyTimeWheelPos, + direction); + // are we blending with a previous, still live transition? + if (this.lastTransition !== nullTransition) { + lastTranslations = this.lastTransition.blendTranslations(this.lastFrequencyTimeWheelPos, + this.lastDirection); + } else { + lastTranslations = animationOperations.calculateTranslations(this.lastAnimation, + this.lastFrequencyTimeWheelPos, + this.lastDirection); + } + + // blend last / next translations + nextTranslations = Vec3.multiply(this.filteredProgress, nextTranslations); + lastTranslations = Vec3.multiply((1 - this.filteredProgress), lastTranslations); + nextTranslations = Vec3.sum(nextTranslations, lastTranslations); + + if (this.liveReachPoses.length > 0) { + for (pose in this.liveReachPoses) { + var reachPoseStrength = this.liveReachPoses[pose].currentStrength(); + var poseTranslations = animationOperations.calculateTranslations( + this.liveReachPoses[pose].reachPoseDataFile, + frequencyTimeWheelPos, + direction); + + // can't use Vec3 operations here, as if x,y or z is zero, the reachPose should have no influence at all + if (Math.abs(poseTranslations.x) > 0) { + nextTranslations.x = reachPoseStrength * poseTranslations.x + (1 - reachPoseStrength) * nextTranslations.x; + } + if (Math.abs(poseTranslations.y) > 0) { + nextTranslations.y = reachPoseStrength * poseTranslations.y + (1 - reachPoseStrength) * nextTranslations.y; + } + if (Math.abs(poseTranslations.z) > 0) { + nextTranslations.z = reachPoseStrength * poseTranslations.z + (1 - reachPoseStrength) * nextTranslations.z; + } + } + } + return nextTranslations; + }; + + this.blendRotations = function(jointName, frequencyTimeWheelPos, direction) { + var lastRotations = {x:0, y:0, z:0}; + var nextRotations = animationOperations.calculateRotations(jointName, + this.nextAnimation, + frequencyTimeWheelPos, + direction); + + // are we blending with a previous, still live transition? + if (this.lastTransition !== nullTransition) { + lastRotations = this.lastTransition.blendRotations(jointName, + this.lastFrequencyTimeWheelPos, + this.lastDirection); + } else { + lastRotations = animationOperations.calculateRotations(jointName, + this.lastAnimation, + this.lastFrequencyTimeWheelPos, + this.lastDirection); + } + // blend last / next translations + nextRotations = Vec3.multiply(this.filteredProgress, nextRotations); + lastRotations = Vec3.multiply((1 - this.filteredProgress), lastRotations); + nextRotations = Vec3.sum(nextRotations, lastRotations); + + // are there reachPoses defined for this transition? + if (this.liveReachPoses.length > 0) { + for (pose in this.liveReachPoses) { + var reachPoseStrength = this.liveReachPoses[pose].currentStrength(); + var poseRotations = animationOperations.calculateRotations(jointName, + this.liveReachPoses[pose].reachPoseDataFile, + frequencyTimeWheelPos, + direction); + + // don't use Vec3 operations here, as if x,y or z is zero, the reach pose should have no influence at all + if (Math.abs(poseRotations.x) > 0) { + nextRotations.x = reachPoseStrength * poseRotations.x + (1 - reachPoseStrength) * nextRotations.x; + } + if (Math.abs(poseRotations.y) > 0) { + nextRotations.y = reachPoseStrength * poseRotations.y + (1 - reachPoseStrength) * nextRotations.y; + } + if (Math.abs(poseRotations.z) > 0) { + nextRotations.z = reachPoseStrength * poseRotations.z + (1 - reachPoseStrength) * nextRotations.z; + } + } + } + return nextRotations; + }; +}; // end Transition constructor + +// individual joint modifiers +FrequencyMultipliers = function(joint, direction) { + // gather multipliers + this.pitchFrequencyMultiplier = 1; + this.yawFrequencyMultiplier = 1; + this.rollFrequencyMultiplier = 1; + this.swayFrequencyMultiplier = 1; + this.bobFrequencyMultiplier = 1; + this.thrustFrequencyMultiplier = 1; + + if (joint) { + if (joint.pitchFrequencyMultiplier) { + this.pitchFrequencyMultiplier = joint.pitchFrequencyMultiplier; + } + if (joint.yawFrequencyMultiplier) { + this.yawFrequencyMultiplier = joint.yawFrequencyMultiplier; + } + if (joint.rollFrequencyMultiplier) { + this.rollFrequencyMultiplier = joint.rollFrequencyMultiplier; + } + if (joint.swayFrequencyMultiplier) { + this.swayFrequencyMultiplier = joint.swayFrequencyMultiplier; + } + if (joint.bobFrequencyMultiplier) { + this.bobFrequencyMultiplier = joint.bobFrequencyMultiplier; + } + if (joint.thrustFrequencyMultiplier) { + this.thrustFrequencyMultiplier = joint.thrustFrequencyMultiplier; + } + } +}; diff --git a/examples/libraries/walkConstants.js b/examples/libraries/walkConstants.js index abc9a4bc5f..379986e79b 100644 --- a/examples/libraries/walkConstants.js +++ b/examples/libraries/walkConstants.js @@ -1,54 +1,54 @@ -// -// walkConstants.js -// version 1.0 -// -// Created by David Wooldridge, June 2015 -// Copyright © 2015 High Fidelity, Inc. -// -// Provides constants necessary for the operation of the walk.js script and the walkApi.js script -// -// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// locomotion states -STATIC = 1; -SURFACE_MOTION = 2; -AIR_MOTION = 4; - -// directions -UP = 1; -DOWN = 2; -LEFT = 4; -RIGHT = 8; -FORWARDS = 16; -BACKWARDS = 32; -NONE = 64; - -// waveshapes -SAWTOOTH = 1; -TRIANGLE = 2; -SQUARE = 4; - -// used by walk.js and walkApi.js -MAX_WALK_SPEED = 2.9; // peak, by observation -MAX_FT_WHEEL_INCREMENT = 25; // avoid fast walk when landing -TOP_SPEED = 300; -ON_SURFACE_THRESHOLD = 0.1; // height above surface to be considered as on the surface -TRANSITION_COMPLETE = 1000; -PITCH_MAX = 60; // maximum speed induced pitch -ROLL_MAX = 80; // maximum speed induced leaning / banking -DELTA_YAW_MAX = 1.7; // maximum change in yaw in rad/s - -// used by walkApi.js only -MOVE_THRESHOLD = 0.075; // movement dead zone -ACCELERATION_THRESHOLD = 0.2; // detect stop to walking -DECELERATION_THRESHOLD = -6; // detect walking to stop -FAST_DECELERATION_THRESHOLD = -150; // detect flying to stop -BOUNCE_ACCELERATION_THRESHOLD = 25; // used to ignore gravity influence fluctuations after landing -GRAVITY_THRESHOLD = 3.0; // height above surface where gravity is in effect -OVERCOME_GRAVITY_SPEED = 0.5; // reaction sensitivity to jumping under gravity -LANDING_THRESHOLD = 0.35; // metres from a surface below which need to prepare for impact -MAX_TRANSITION_RECURSION = 10; // how many nested transitions are permitted \ No newline at end of file +// +// walkConstants.js +// version 1.0 +// +// Created by David Wooldridge, June 2015 +// Copyright © 2015 High Fidelity, Inc. +// +// Provides constants necessary for the operation of the walk.js script and the walkApi.js script +// +// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// locomotion states +STATIC = 1; +SURFACE_MOTION = 2; +AIR_MOTION = 4; + +// directions +UP = 1; +DOWN = 2; +LEFT = 4; +RIGHT = 8; +FORWARDS = 16; +BACKWARDS = 32; +NONE = 64; + +// waveshapes +SAWTOOTH = 1; +TRIANGLE = 2; +SQUARE = 4; + +// used by walk.js and walkApi.js +MAX_WALK_SPEED = 2.9; // peak, by observation +MAX_FT_WHEEL_INCREMENT = 25; // avoid fast walk when landing +TOP_SPEED = 300; +ON_SURFACE_THRESHOLD = 0.1; // height above surface to be considered as on the surface +TRANSITION_COMPLETE = 1000; +PITCH_MAX = 60; // maximum speed induced pitch +ROLL_MAX = 80; // maximum speed induced leaning / banking +DELTA_YAW_MAX = 1.7; // maximum change in yaw in rad/s + +// used by walkApi.js only +MOVE_THRESHOLD = 0.075; // movement dead zone +ACCELERATION_THRESHOLD = 0.2; // detect stop to walking +DECELERATION_THRESHOLD = -6; // detect walking to stop +FAST_DECELERATION_THRESHOLD = -150; // detect flying to stop +BOUNCE_ACCELERATION_THRESHOLD = 25; // used to ignore gravity influence fluctuations after landing +GRAVITY_THRESHOLD = 3.0; // height above surface where gravity is in effect +OVERCOME_GRAVITY_SPEED = 0.5; // reaction sensitivity to jumping under gravity +LANDING_THRESHOLD = 0.35; // metres from a surface below which need to prepare for impact +MAX_TRANSITION_RECURSION = 10; // how many nested transitions are permitted diff --git a/examples/libraries/walkFilters.js b/examples/libraries/walkFilters.js index 1f8077d9eb..90dee5512f 100644 --- a/examples/libraries/walkFilters.js +++ b/examples/libraries/walkFilters.js @@ -1,204 +1,204 @@ -// -// walkFilters.js -// version 1.1 -// -// Created by David Wooldridge, June 2015 -// Copyright © 2014 - 2015 High Fidelity, Inc. -// -// Provides a variety of filters for use by the walk.js script v1.2+ -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// simple averaging (LP) filter for damping / smoothing -AveragingFilter = function(length) { - // initialise the array of past values - this.pastValues = []; - for (var i = 0; i < length; i++) { - this.pastValues.push(0); - } - // single arg is the nextInputValue - this.process = function() { - if (this.pastValues.length === 0 && arguments[0]) { - return arguments[0]; - } else if (arguments[0] !== null) { - this.pastValues.push(arguments[0]); - this.pastValues.shift(); - var nextOutputValue = 0; - for (var value in this.pastValues) nextOutputValue += this.pastValues[value]; - return nextOutputValue / this.pastValues.length; - } else { - return 0; - } - }; -}; - -// 2nd order 2Hz Butterworth LP filter -ButterworthFilter = function() { - // coefficients calculated at: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html - this.gain = 104.9784742; - this.coeffOne = -0.7436551950; - this.coeffTwo = 1.7055521455; - - // initialise the arrays - this.xv = []; - this.yv = []; - for (var i = 0; i < 3; i++) { - this.xv.push(0); - this.yv.push(0); - } - - // process values - this.process = function(nextInputValue) { - this.xv[0] = this.xv[1]; - this.xv[1] = this.xv[2]; - this.xv[2] = nextInputValue / this.gain; - this.yv[0] = this.yv[1]; - this.yv[1] = this.yv[2]; - this.yv[2] = (this.xv[0] + this.xv[2]) + - 2 * this.xv[1] + - (this.coeffOne * this.yv[0]) + - (this.coeffTwo * this.yv[1]); - return this.yv[2]; - }; -}; // end Butterworth filter constructor - -// Add harmonics to a given sine wave to form square, sawtooth or triangle waves -// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html -WaveSynth = function(waveShape, numHarmonics, smoothing) { - this.numHarmonics = numHarmonics; - this.waveShape = waveShape; - this.smoothingFilter = new AveragingFilter(smoothing); - - // NB: frequency in radians - this.calculate = function(frequency) { - // make some shapes - var harmonics = 0; - var multiplier = 0; - var iterations = this.numHarmonics * 2 + 2; - if (this.waveShape === TRIANGLE) { - iterations++; - } - for (var n = 1; n < iterations; n++) { - switch (this.waveShape) { - case SAWTOOTH: { - multiplier = 1 / n; - harmonics += multiplier * Math.sin(n * frequency); - break; - } - - case TRIANGLE: { - if (n % 2 === 1) { - var mulitplier = 1 / (n * n); - // multiply (4n-1)th harmonics by -1 - if (n === 3 || n === 7 || n === 11 || n === 15) { - mulitplier *= -1; - } - harmonics += mulitplier * Math.sin(n * frequency); - } - break; - } - - case SQUARE: { - if (n % 2 === 1) { - multiplier = 1 / n; - harmonics += multiplier * Math.sin(n * frequency); - } - break; - } - } - } - // smooth the result and return - return this.smoothingFilter.process(harmonics); - }; -}; - -// Create a motion wave by summing pre-calculated harmonics (Fourier synthesis) -HarmonicsFilter = function(magnitudes, phaseAngles) { - this.magnitudes = magnitudes; - this.phaseAngles = phaseAngles; - - this.calculate = function(twoPiFT) { - var harmonics = 0; - var numHarmonics = magnitudes.length; - for (var n = 0; n < numHarmonics; n++) { - harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]); - } - return harmonics; - }; -}; - -// the main filter object literal -filter = (function() { - - const HALF_CYCLE = 180; - - // Bezier private variables - var _C1 = {x:0, y:0}; - var _C4 = {x:1, y:1}; - - // Bezier private functions - function _B1(t) { return t * t * t }; - function _B2(t) { return 3 * t * t * (1 - t) }; - function _B3(t) { return 3 * t * (1 - t) * (1 - t) }; - function _B4(t) { return (1 - t) * (1 - t) * (1 - t) }; - - return { - - // helper methods - degToRad: function(degrees) { - var convertedValue = degrees * Math.PI / HALF_CYCLE; - return convertedValue; - }, - - radToDeg: function(radians) { - var convertedValue = radians * HALF_CYCLE / Math.PI; - return convertedValue; - }, - - // these filters need instantiating, as they hold arrays of previous values - - // simple averaging (LP) filter for damping / smoothing - createAveragingFilter: function(length) { - var newAveragingFilter = new AveragingFilter(length); - return newAveragingFilter; - }, - - // provides LP filtering with improved frequency / phase response - createButterworthFilter: function() { - var newButterworthFilter = new ButterworthFilter(); - return newButterworthFilter; - }, - - // generates sawtooth, triangle or square waves using harmonics - createWaveSynth: function(waveShape, numHarmonics, smoothing) { - var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing); - return newWaveSynth; - }, - - // generates arbitrary waveforms using pre-calculated harmonics - createHarmonicsFilter: function(magnitudes, phaseAngles) { - var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles); - return newHarmonicsFilter; - }, - - // the following filters do not need separate instances, as they hold no previous values - - // Bezier response curve shaping for more natural transitions - bezier: function(input, C2, C3) { - // based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/ - input = 1 - input; - return _C1.y * _B1(input) + C2.y * _B2(input) + C3.y * _B3(input) + _C4.y * _B4(input); - }, - - // simple clipping filter (special case for hips y-axis skeleton offset for walk animation) - clipTrough: function(inputValue, peak, strength) { - var outputValue = inputValue * strength; - if (outputValue < -peak) { - outputValue = -peak; - } - return outputValue; - } - } -})(); \ No newline at end of file +// +// walkFilters.js +// version 1.1 +// +// Created by David Wooldridge, June 2015 +// Copyright © 2014 - 2015 High Fidelity, Inc. +// +// Provides a variety of filters for use by the walk.js script v1.2+ +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// simple averaging (LP) filter for damping / smoothing +AveragingFilter = function(length) { + // initialise the array of past values + this.pastValues = []; + for (var i = 0; i < length; i++) { + this.pastValues.push(0); + } + // single arg is the nextInputValue + this.process = function() { + if (this.pastValues.length === 0 && arguments[0]) { + return arguments[0]; + } else if (arguments[0] !== null) { + this.pastValues.push(arguments[0]); + this.pastValues.shift(); + var nextOutputValue = 0; + for (var value in this.pastValues) nextOutputValue += this.pastValues[value]; + return nextOutputValue / this.pastValues.length; + } else { + return 0; + } + }; +}; + +// 2nd order 2Hz Butterworth LP filter +ButterworthFilter = function() { + // coefficients calculated at: http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html + this.gain = 104.9784742; + this.coeffOne = -0.7436551950; + this.coeffTwo = 1.7055521455; + + // initialise the arrays + this.xv = []; + this.yv = []; + for (var i = 0; i < 3; i++) { + this.xv.push(0); + this.yv.push(0); + } + + // process values + this.process = function(nextInputValue) { + this.xv[0] = this.xv[1]; + this.xv[1] = this.xv[2]; + this.xv[2] = nextInputValue / this.gain; + this.yv[0] = this.yv[1]; + this.yv[1] = this.yv[2]; + this.yv[2] = (this.xv[0] + this.xv[2]) + + 2 * this.xv[1] + + (this.coeffOne * this.yv[0]) + + (this.coeffTwo * this.yv[1]); + return this.yv[2]; + }; +}; // end Butterworth filter constructor + +// Add harmonics to a given sine wave to form square, sawtooth or triangle waves +// Geometric wave synthesis fundamentals taken from: http://hyperphysics.phy-astr.gsu.edu/hbase/audio/geowv.html +WaveSynth = function(waveShape, numHarmonics, smoothing) { + this.numHarmonics = numHarmonics; + this.waveShape = waveShape; + this.smoothingFilter = new AveragingFilter(smoothing); + + // NB: frequency in radians + this.calculate = function(frequency) { + // make some shapes + var harmonics = 0; + var multiplier = 0; + var iterations = this.numHarmonics * 2 + 2; + if (this.waveShape === TRIANGLE) { + iterations++; + } + for (var n = 1; n < iterations; n++) { + switch (this.waveShape) { + case SAWTOOTH: { + multiplier = 1 / n; + harmonics += multiplier * Math.sin(n * frequency); + break; + } + + case TRIANGLE: { + if (n % 2 === 1) { + var mulitplier = 1 / (n * n); + // multiply (4n-1)th harmonics by -1 + if (n === 3 || n === 7 || n === 11 || n === 15) { + mulitplier *= -1; + } + harmonics += mulitplier * Math.sin(n * frequency); + } + break; + } + + case SQUARE: { + if (n % 2 === 1) { + multiplier = 1 / n; + harmonics += multiplier * Math.sin(n * frequency); + } + break; + } + } + } + // smooth the result and return + return this.smoothingFilter.process(harmonics); + }; +}; + +// Create a motion wave by summing pre-calculated harmonics (Fourier synthesis) +HarmonicsFilter = function(magnitudes, phaseAngles) { + this.magnitudes = magnitudes; + this.phaseAngles = phaseAngles; + + this.calculate = function(twoPiFT) { + var harmonics = 0; + var numHarmonics = magnitudes.length; + for (var n = 0; n < numHarmonics; n++) { + harmonics += this.magnitudes[n] * Math.cos(n * twoPiFT - this.phaseAngles[n]); + } + return harmonics; + }; +}; + +// the main filter object literal +filter = (function() { + + const HALF_CYCLE = 180; + + // Bezier private variables + var _C1 = {x:0, y:0}; + var _C4 = {x:1, y:1}; + + // Bezier private functions + function _B1(t) { return t * t * t }; + function _B2(t) { return 3 * t * t * (1 - t) }; + function _B3(t) { return 3 * t * (1 - t) * (1 - t) }; + function _B4(t) { return (1 - t) * (1 - t) * (1 - t) }; + + return { + + // helper methods + degToRad: function(degrees) { + var convertedValue = degrees * Math.PI / HALF_CYCLE; + return convertedValue; + }, + + radToDeg: function(radians) { + var convertedValue = radians * HALF_CYCLE / Math.PI; + return convertedValue; + }, + + // these filters need instantiating, as they hold arrays of previous values + + // simple averaging (LP) filter for damping / smoothing + createAveragingFilter: function(length) { + var newAveragingFilter = new AveragingFilter(length); + return newAveragingFilter; + }, + + // provides LP filtering with improved frequency / phase response + createButterworthFilter: function() { + var newButterworthFilter = new ButterworthFilter(); + return newButterworthFilter; + }, + + // generates sawtooth, triangle or square waves using harmonics + createWaveSynth: function(waveShape, numHarmonics, smoothing) { + var newWaveSynth = new WaveSynth(waveShape, numHarmonics, smoothing); + return newWaveSynth; + }, + + // generates arbitrary waveforms using pre-calculated harmonics + createHarmonicsFilter: function(magnitudes, phaseAngles) { + var newHarmonicsFilter = new HarmonicsFilter(magnitudes, phaseAngles); + return newHarmonicsFilter; + }, + + // the following filters do not need separate instances, as they hold no previous values + + // Bezier response curve shaping for more natural transitions + bezier: function(input, C2, C3) { + // based on script by Dan Pupius (www.pupius.net) http://13thparallel.com/archive/bezier-curves/ + input = 1 - input; + return _C1.y * _B1(input) + C2.y * _B2(input) + C3.y * _B3(input) + _C4.y * _B4(input); + }, + + // simple clipping filter (special case for hips y-axis skeleton offset for walk animation) + clipTrough: function(inputValue, peak, strength) { + var outputValue = inputValue * strength; + if (outputValue < -peak) { + outputValue = -peak; + } + return outputValue; + } + } +})(); diff --git a/examples/libraries/walkSettings.js b/examples/libraries/walkSettings.js index e997e4f29e..3e5ac53572 100644 --- a/examples/libraries/walkSettings.js +++ b/examples/libraries/walkSettings.js @@ -1,97 +1,97 @@ -// -// walkSettings.js -// version 0.1 -// -// Created by David Wooldridge, June 2015 -// Copyright © 2015 High Fidelity, Inc. -// -// Presents settings for walk.js -// -// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -WalkSettings = function() { - var _visible = false; - var _innerWidth = Window.innerWidth; - const MARGIN_RIGHT = 58; - const MARGIN_TOP = 145; - const ICON_SIZE = 50; - const ICON_ALPHA = 0.9; - - var minimisedTab = Overlays.addOverlay("image", { - x: _innerWidth - MARGIN_RIGHT, y: Window.innerHeight - MARGIN_TOP, - width: ICON_SIZE, height: ICON_SIZE, - imageURL: pathToAssets + 'overlay-images/ddpa-minimised-ddpa-tab.png', - visible: true, alpha: ICON_ALPHA - }); - - function mousePressEvent(event) { - if (Overlays.getOverlayAtPoint(event) === minimisedTab) { - _visible = !_visible; - _webWindow.setVisible(_visible); - } - } - Controller.mousePressEvent.connect(mousePressEvent); - - Script.update.connect(function(deltaTime) { - if (window.innerWidth !== _innerWidth) { - _innerWidth = window.innerWidth; - Overlays.EditOverlay(minimisedTab, {x: _innerWidth - MARGIN_RIGHT}); - } - }); - - function cleanup() { - Overlays.deleteOverlay(minimisedTab); - } - Script.scriptEnding.connect(cleanup); - - var _shift = false; - function keyPressEvent(event) { - if (event.text === "SHIFT") { - _shift = true; - } - if (_shift && (event.text === 'o' || event.text === 'O')) { - _visible = !_visible; - _webWindow.setVisible(_visible); - } - } - function keyReleaseEvent(event) { - if (event.text === "SHIFT") { - _shift = false; - } - } - Controller.keyPressEvent.connect(keyPressEvent); - Controller.keyReleaseEvent.connect(keyReleaseEvent); - - // web window - const PANEL_WIDTH = 200; - const PANEL_HEIGHT = 180; - var _url = Script.resolvePath('html/walkSettings.html'); - var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false); - _webWindow.setVisible(false); - _webWindow.eventBridge.webEventReceived.connect(function(data) { - data = JSON.parse(data); - - if (data.type == "init") { - // send the current settings to the window - _webWindow.eventBridge.emitScriptEvent(JSON.stringify({ - type: "update", - armsFree: avatar.armsFree, - makesFootStepSounds: avatar.makesFootStepSounds, - blenderPreRotations: avatar.blenderPreRotations - })); - } else if (data.type == "powerToggle") { - motion.isLive = !motion.isLive; - } else if (data.type == "update") { - // receive settings from the window - avatar.armsFree = data.armsFree; - avatar.makesFootStepSounds = data.makesFootStepSounds; - avatar.blenderPreRotations = data.blenderPreRotations; - } - }); -}; - -walkSettings = WalkSettings(); \ No newline at end of file +// +// walkSettings.js +// version 0.1 +// +// Created by David Wooldridge, June 2015 +// Copyright © 2015 High Fidelity, Inc. +// +// Presents settings for walk.js +// +// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +WalkSettings = function() { + var _visible = false; + var _innerWidth = Window.innerWidth; + const MARGIN_RIGHT = 58; + const MARGIN_TOP = 145; + const ICON_SIZE = 50; + const ICON_ALPHA = 0.9; + + var minimisedTab = Overlays.addOverlay("image", { + x: _innerWidth - MARGIN_RIGHT, y: Window.innerHeight - MARGIN_TOP, + width: ICON_SIZE, height: ICON_SIZE, + imageURL: pathToAssets + 'overlay-images/ddpa-minimised-ddpa-tab.png', + visible: true, alpha: ICON_ALPHA + }); + + function mousePressEvent(event) { + if (Overlays.getOverlayAtPoint(event) === minimisedTab) { + _visible = !_visible; + _webWindow.setVisible(_visible); + } + } + Controller.mousePressEvent.connect(mousePressEvent); + + Script.update.connect(function(deltaTime) { + if (window.innerWidth !== _innerWidth) { + _innerWidth = window.innerWidth; + Overlays.EditOverlay(minimisedTab, {x: _innerWidth - MARGIN_RIGHT}); + } + }); + + function cleanup() { + Overlays.deleteOverlay(minimisedTab); + } + Script.scriptEnding.connect(cleanup); + + var _shift = false; + function keyPressEvent(event) { + if (event.text === "SHIFT") { + _shift = true; + } + if (_shift && (event.text === 'o' || event.text === 'O')) { + _visible = !_visible; + _webWindow.setVisible(_visible); + } + } + function keyReleaseEvent(event) { + if (event.text === "SHIFT") { + _shift = false; + } + } + Controller.keyPressEvent.connect(keyPressEvent); + Controller.keyReleaseEvent.connect(keyReleaseEvent); + + // web window + const PANEL_WIDTH = 200; + const PANEL_HEIGHT = 180; + var _url = Script.resolvePath('html/walkSettings.html'); + var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false); + _webWindow.setVisible(false); + _webWindow.eventBridge.webEventReceived.connect(function(data) { + data = JSON.parse(data); + + if (data.type == "init") { + // send the current settings to the window + _webWindow.eventBridge.emitScriptEvent(JSON.stringify({ + type: "update", + armsFree: avatar.armsFree, + makesFootStepSounds: avatar.makesFootStepSounds, + blenderPreRotations: avatar.blenderPreRotations + })); + } else if (data.type == "powerToggle") { + motion.isLive = !motion.isLive; + } else if (data.type == "update") { + // receive settings from the window + avatar.armsFree = data.armsFree; + avatar.makesFootStepSounds = data.makesFootStepSounds; + avatar.blenderPreRotations = data.blenderPreRotations; + } + }); +}; + +walkSettings = WalkSettings(); diff --git a/examples/toys/flashlight/createFlashlight.js b/examples/toys/flashlight/createFlashlight.js index 73b10d3189..490792027a 100644 --- a/examples/toys/flashlight/createFlashlight.js +++ b/examples/toys/flashlight/createFlashlight.js @@ -1,32 +1,32 @@ -// -// createFlashlight.js -// examples/entityScripts -// -// Created by Sam Gateau on 9/9/15. -// Copyright 2015 High Fidelity, Inc. -// -// This is a toy script that create a flashlight entity that lit when grabbed -// This can be run from an interface and the flashlight will get deleted from the domain when quitting -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ -Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js"); - - -var scriptURL = Script.resolvePath('flashlight.js?123123'); - -var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; - -var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); - -var flashlight = Entities.addEntity({ - type: "Model", - modelURL: modelURL, - position: center, - dimensions: { x: 0.08, y: 0.30, z: 0.08}, - collisionsWillMove: true, - shapeType: 'box', - script: scriptURL -}); +// +// createFlashlight.js +// examples/entityScripts +// +// Created by Sam Gateau on 9/9/15. +// Copyright 2015 High Fidelity, Inc. +// +// This is a toy script that create a flashlight entity that lit when grabbed +// This can be run from an interface and the flashlight will get deleted from the domain when quitting +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ +Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js"); + + +var scriptURL = Script.resolvePath('flashlight.js?123123'); + +var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx"; + +var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); + +var flashlight = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: center, + dimensions: { x: 0.08, y: 0.30, z: 0.08}, + collisionsWillMove: true, + shapeType: 'box', + script: scriptURL +}); diff --git a/examples/toys/flashlight/flashlight.js b/examples/toys/flashlight/flashlight.js index a775f185e3..722a5c1bfc 100644 --- a/examples/toys/flashlight/flashlight.js +++ b/examples/toys/flashlight/flashlight.js @@ -1,269 +1,269 @@ -// -// flashlight.js -// -// Script Type: Entity -// -// Created by Sam Gateau on 9/9/15. -// Additions by James B. Pollack @imgntn on 9/21/2015 -// Copyright 2015 High Fidelity, Inc. -// -// This is a toy script that can be added to the Flashlight model entity: -// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx" -// that creates a spotlight attached with the flashlight model while the entity is grabbed -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ -(function() { - - Script.include("../../libraries/utils.js"); - - var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav'; - var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav'; - - //we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them - var startTime = Date.now(); - //if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value. - var LIFETIME = 25; - var MSEC_PER_SEC = 1000.0; - - // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember - // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) - function Flashlight() { - return; - } - - //if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will - var DISABLE_LIGHT_THRESHOLD = 0.7; - - // These constants define the Spotlight position and orientation relative to the model - var MODEL_LIGHT_POSITION = { - x: 0, - y: -0.3, - z: 0 - }; - var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { - x: 1, - y: 0, - z: 0 - }); - - var GLOW_LIGHT_POSITION = { - x: 0, - y: -0.1, - z: 0 - }; - - // Evaluate the world light entity positions and orientations from the model ones - function evalLightWorldTransform(modelPos, modelRot) { - - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - } - - function glowLightWorldTransform(modelPos, modelRot) { - return { - p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)), - q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) - }; - } - - Flashlight.prototype = { - lightOn: false, - hand: null, - whichHand: null, - hasSpotlight: false, - spotlight: null, - setRightHand: function() { - this.hand = 'RIGHT'; - }, - - setLeftHand: function() { - this.hand = 'LEFT'; - }, - - startNearGrab: function() { - if (!this.hasSpotlight) { - - //this light casts the beam - this.spotlight = Entities.addEntity({ - type: "Light", - isSpotlight: true, - dimensions: { - x: 2, - y: 2, - z: 20 - }, - color: { - red: 255, - green: 255, - blue: 255 - }, - intensity: 2, - exponent: 0.3, - cutoff: 20, - lifetime: LIFETIME - }); - - //this light creates the effect of a bulb at the end of the flashlight - this.glowLight = Entities.addEntity({ - type: "Light", - dimensions: { - x: 0.25, - y: 0.25, - z: 0.25 - }, - isSpotlight: false, - color: { - red: 255, - green: 255, - blue: 255 - }, - exponent: 0, - cutoff: 90, // in degrees - lifetime: LIFETIME - }); - - this.hasSpotlight = true; - - } - - }, - - setWhichHand: function() { - this.whichHand = this.hand; - }, - - continueNearGrab: function() { - if (this.whichHand === null) { - //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten - this.setWhichHand(); - } else { - this.updateLightPositions(); - this.changeLightWithTriggerPressure(this.whichHand); - } - }, - - releaseGrab: function() { - //delete the lights and reset state - if (this.hasSpotlight) { - Entities.deleteEntity(this.spotlight); - Entities.deleteEntity(this.glowLight); - this.hasSpotlight = false; - this.glowLight = null; - this.spotlight = null; - this.whichHand = null; - this.lightOn = false; - } - }, - - updateLightPositions: function() { - var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); - - //move the two lights along the vectors we set above - var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); - var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation); - - //move them with the entity model - Entities.editEntity(this.spotlight, { - position: lightTransform.p, - rotation: lightTransform.q, - lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME - }); - - Entities.editEntity(this.glowLight, { - position: glowLightTransform.p, - rotation: glowLightTransform.q, - lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME - }); - - }, - - changeLightWithTriggerPressure: function(flashLightHand) { - var handClickString = flashLightHand + "_HAND_CLICK"; - - var handClick = Controller.findAction(handClickString); - - this.triggerValue = Controller.getActionValue(handClick); - - if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) { - this.turnLightOff(); - } else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) { - this.turnLightOn(); - } - return; - }, - - turnLightOff: function() { - this.playSoundAtCurrentPosition(false); - Entities.editEntity(this.spotlight, { - intensity: 0 - }); - Entities.editEntity(this.glowLight, { - intensity: 0 - }); - this.lightOn = false; - }, - - turnLightOn: function() { - this.playSoundAtCurrentPosition(true); - - Entities.editEntity(this.glowLight, { - intensity: 2 - }); - Entities.editEntity(this.spotlight, { - intensity: 2 - }); - this.lightOn = true; - }, - - playSoundAtCurrentPosition: function(playOnSound) { - var position = Entities.getEntityProperties(this.entityID, "position").position; - - var audioProperties = { - volume: 0.25, - position: position - }; - - if (playOnSound) { - Audio.playSound(this.ON_SOUND, audioProperties); - } else { - Audio.playSound(this.OFF_SOUND, audioProperties); - } - }, - - preload: function(entityID) { - - // preload() will be called when the entity has become visible (or known) to the interface - // it gives us a chance to set our local JavaScript object up. In this case it means: - // * remembering our entityID, so we can access it in cases where we're called without an entityID - // * preloading sounds - this.entityID = entityID; - this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL); - this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL); - - }, - - unload: function() { - // unload() will be called when our entity is no longer available. It may be because we were deleted, - // or because we've left the domain or quit the application. - if (this.hasSpotlight) { - Entities.deleteEntity(this.spotlight); - Entities.deleteEntity(this.glowLight); - this.hasSpotlight = false; - this.glowLight = null; - this.spotlight = null; - this.whichHand = null; - this.lightOn = false; - } - - }, - - }; - - // entity scripts always need to return a newly constructed object of our type - return new Flashlight(); -}); \ No newline at end of file +// +// flashlight.js +// +// Script Type: Entity +// +// Created by Sam Gateau on 9/9/15. +// Additions by James B. Pollack @imgntn on 9/21/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This is a toy script that can be added to the Flashlight model entity: +// "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx" +// that creates a spotlight attached with the flashlight model while the entity is grabbed +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ +(function() { + + Script.include("../../libraries/utils.js"); + + var ON_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_on.wav'; + var OFF_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/flashlight_off.wav'; + + //we are creating lights that we don't want to get stranded so lets make sure that we can get rid of them + var startTime = Date.now(); + //if you're going to be using this in a dungeon or something and holding it for a long time, increase this lifetime value. + var LIFETIME = 25; + var MSEC_PER_SEC = 1000.0; + + // this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember + // our this object, so we can access it in cases where we're called without a this (like in the case of various global signals) + function Flashlight() { + return; + } + + //if the trigger value goes below this while held, the flashlight will turn off. if it goes above, it will + var DISABLE_LIGHT_THRESHOLD = 0.7; + + // These constants define the Spotlight position and orientation relative to the model + var MODEL_LIGHT_POSITION = { + x: 0, + y: -0.3, + z: 0 + }; + var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, + y: 0, + z: 0 + }); + + var GLOW_LIGHT_POSITION = { + x: 0, + y: -0.1, + z: 0 + }; + + // Evaluate the world light entity positions and orientations from the model ones + function evalLightWorldTransform(modelPos, modelRot) { + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + } + + function glowLightWorldTransform(modelPos, modelRot) { + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, GLOW_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; + } + + Flashlight.prototype = { + lightOn: false, + hand: null, + whichHand: null, + hasSpotlight: false, + spotlight: null, + setRightHand: function() { + this.hand = 'RIGHT'; + }, + + setLeftHand: function() { + this.hand = 'LEFT'; + }, + + startNearGrab: function() { + if (!this.hasSpotlight) { + + //this light casts the beam + this.spotlight = Entities.addEntity({ + type: "Light", + isSpotlight: true, + dimensions: { + x: 2, + y: 2, + z: 20 + }, + color: { + red: 255, + green: 255, + blue: 255 + }, + intensity: 2, + exponent: 0.3, + cutoff: 20, + lifetime: LIFETIME + }); + + //this light creates the effect of a bulb at the end of the flashlight + this.glowLight = Entities.addEntity({ + type: "Light", + dimensions: { + x: 0.25, + y: 0.25, + z: 0.25 + }, + isSpotlight: false, + color: { + red: 255, + green: 255, + blue: 255 + }, + exponent: 0, + cutoff: 90, // in degrees + lifetime: LIFETIME + }); + + this.hasSpotlight = true; + + } + + }, + + setWhichHand: function() { + this.whichHand = this.hand; + }, + + continueNearGrab: function() { + if (this.whichHand === null) { + //only set the active hand once -- if we always read the current hand, our 'holding' hand will get overwritten + this.setWhichHand(); + } else { + this.updateLightPositions(); + this.changeLightWithTriggerPressure(this.whichHand); + } + }, + + releaseGrab: function() { + //delete the lights and reset state + if (this.hasSpotlight) { + Entities.deleteEntity(this.spotlight); + Entities.deleteEntity(this.glowLight); + this.hasSpotlight = false; + this.glowLight = null; + this.spotlight = null; + this.whichHand = null; + this.lightOn = false; + } + }, + + updateLightPositions: function() { + var modelProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); + + //move the two lights along the vectors we set above + var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + var glowLightTransform = glowLightWorldTransform(modelProperties.position, modelProperties.rotation); + + //move them with the entity model + Entities.editEntity(this.spotlight, { + position: lightTransform.p, + rotation: lightTransform.q, + lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME + }); + + Entities.editEntity(this.glowLight, { + position: glowLightTransform.p, + rotation: glowLightTransform.q, + lifetime: (Date.now() - startTime) / MSEC_PER_SEC + LIFETIME + }); + + }, + + changeLightWithTriggerPressure: function(flashLightHand) { + var handClickString = flashLightHand + "_HAND_CLICK"; + + var handClick = Controller.findAction(handClickString); + + this.triggerValue = Controller.getActionValue(handClick); + + if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) { + this.turnLightOff(); + } else if (this.triggerValue >= DISABLE_LIGHT_THRESHOLD && this.lightOn === false) { + this.turnLightOn(); + } + return; + }, + + turnLightOff: function() { + this.playSoundAtCurrentPosition(false); + Entities.editEntity(this.spotlight, { + intensity: 0 + }); + Entities.editEntity(this.glowLight, { + intensity: 0 + }); + this.lightOn = false; + }, + + turnLightOn: function() { + this.playSoundAtCurrentPosition(true); + + Entities.editEntity(this.glowLight, { + intensity: 2 + }); + Entities.editEntity(this.spotlight, { + intensity: 2 + }); + this.lightOn = true; + }, + + playSoundAtCurrentPosition: function(playOnSound) { + var position = Entities.getEntityProperties(this.entityID, "position").position; + + var audioProperties = { + volume: 0.25, + position: position + }; + + if (playOnSound) { + Audio.playSound(this.ON_SOUND, audioProperties); + } else { + Audio.playSound(this.OFF_SOUND, audioProperties); + } + }, + + preload: function(entityID) { + + // preload() will be called when the entity has become visible (or known) to the interface + // it gives us a chance to set our local JavaScript object up. In this case it means: + // * remembering our entityID, so we can access it in cases where we're called without an entityID + // * preloading sounds + this.entityID = entityID; + this.ON_SOUND = SoundCache.getSound(ON_SOUND_URL); + this.OFF_SOUND = SoundCache.getSound(OFF_SOUND_URL); + + }, + + unload: function() { + // unload() will be called when our entity is no longer available. It may be because we were deleted, + // or because we've left the domain or quit the application. + if (this.hasSpotlight) { + Entities.deleteEntity(this.spotlight); + Entities.deleteEntity(this.glowLight); + this.hasSpotlight = false; + this.glowLight = null; + this.spotlight = null; + this.whichHand = null; + this.lightOn = false; + } + + }, + + }; + + // entity scripts always need to return a newly constructed object of our type + return new Flashlight(); +}); diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index 49ac923436..3271741985 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -1,87 +1,87 @@ -// -// SunLightExample.js -// examples -// Sam Gateau -// 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("cookies.js"); - -var panel = new Panel(10, 100); - -function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) { - this.subPanel = panel.newSubPanel(name); - - this.subPanel.newSlider("Num Feed", 0, 1, - function(value) { }, - feedGetter, - function(value) { return (value); }); - this.subPanel.newSlider("Num Drawn", 0, 1, - function(value) { }, - drawGetter, - function(value) { return (value); }); - this.subPanel.newSlider("Max Drawn", -1, 1, - capSetter, - capGetter, - function(value) { return (value); }); - - this.update = function () { - var numFeed = this.subPanel.get("Num Feed"); - this.subPanel.set("Num Feed", numFeed); - this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn")); - - var numMax = Math.max(numFeed, 1); - this.subPanel.getWidget("Num Feed").setMaxValue(numMax); - this.subPanel.getWidget("Num Drawn").setMaxValue(numMax); - this.subPanel.getWidget("Max Drawn").setMaxValue(numMax); - }; -}; - -var opaquesCounter = new CounterWidget(panel, "Opaques", - function () { return Scene.getEngineNumFeedOpaqueItems(); }, - function () { return Scene.getEngineNumDrawnOpaqueItems(); }, - function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, - function () { return Scene.getEngineMaxDrawnOpaqueItems(); } -); - -var transparentsCounter = new CounterWidget(panel, "Transparents", - function () { return Scene.getEngineNumFeedTransparentItems(); }, - function () { return Scene.getEngineNumDrawnTransparentItems(); }, - function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, - function () { return Scene.getEngineMaxDrawnTransparentItems(); } -); - -var overlaysCounter = new CounterWidget(panel, "Overlays", - function () { return Scene.getEngineNumFeedOverlay3DItems(); }, - function () { return Scene.getEngineNumDrawnOverlay3DItems(); }, - function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, - function () { return Scene.getEngineMaxDrawnOverlay3DItems(); } -); - - -panel.newCheckbox("Display status", - function(value) { Scene.setEngineDisplayItemStatus(value); }, - function() { return Scene.doEngineDisplayItemStatus(); }, - function(value) { return (value); } -); - -var tickTackPeriod = 500; - -function updateCounters() { - opaquesCounter.update(); - transparentsCounter.update(); - overlaysCounter.update(); -} -Script.setInterval(updateCounters, tickTackPeriod); - -Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); -Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); -Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); - -function scriptEnding() { - panel.destroy(); -} -Script.scriptEnding.connect(scriptEnding); +// +// SunLightExample.js +// examples +// Sam Gateau +// 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("cookies.js"); + +var panel = new Panel(10, 100); + +function CounterWidget(parentPanel, name, feedGetter, drawGetter, capSetter, capGetter) { + this.subPanel = panel.newSubPanel(name); + + this.subPanel.newSlider("Num Feed", 0, 1, + function(value) { }, + feedGetter, + function(value) { return (value); }); + this.subPanel.newSlider("Num Drawn", 0, 1, + function(value) { }, + drawGetter, + function(value) { return (value); }); + this.subPanel.newSlider("Max Drawn", -1, 1, + capSetter, + capGetter, + function(value) { return (value); }); + + this.update = function () { + var numFeed = this.subPanel.get("Num Feed"); + this.subPanel.set("Num Feed", numFeed); + this.subPanel.set("Num Drawn", this.subPanel.get("Num Drawn")); + + var numMax = Math.max(numFeed, 1); + this.subPanel.getWidget("Num Feed").setMaxValue(numMax); + this.subPanel.getWidget("Num Drawn").setMaxValue(numMax); + this.subPanel.getWidget("Max Drawn").setMaxValue(numMax); + }; +}; + +var opaquesCounter = new CounterWidget(panel, "Opaques", + function () { return Scene.getEngineNumFeedOpaqueItems(); }, + function () { return Scene.getEngineNumDrawnOpaqueItems(); }, + function(value) { Scene.setEngineMaxDrawnOpaqueItems(value); }, + function () { return Scene.getEngineMaxDrawnOpaqueItems(); } +); + +var transparentsCounter = new CounterWidget(panel, "Transparents", + function () { return Scene.getEngineNumFeedTransparentItems(); }, + function () { return Scene.getEngineNumDrawnTransparentItems(); }, + function(value) { Scene.setEngineMaxDrawnTransparentItems(value); }, + function () { return Scene.getEngineMaxDrawnTransparentItems(); } +); + +var overlaysCounter = new CounterWidget(panel, "Overlays", + function () { return Scene.getEngineNumFeedOverlay3DItems(); }, + function () { return Scene.getEngineNumDrawnOverlay3DItems(); }, + function(value) { Scene.setEngineMaxDrawnOverlay3DItems(value); }, + function () { return Scene.getEngineMaxDrawnOverlay3DItems(); } +); + + +panel.newCheckbox("Display status", + function(value) { Scene.setEngineDisplayItemStatus(value); }, + function() { return Scene.doEngineDisplayItemStatus(); }, + function(value) { return (value); } +); + +var tickTackPeriod = 500; + +function updateCounters() { + opaquesCounter.update(); + transparentsCounter.update(); + overlaysCounter.update(); +} +Script.setInterval(updateCounters, tickTackPeriod); + +Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); +Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); +Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); + +function scriptEnding() { + panel.destroy(); +} +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/walk.js b/examples/walk.js index 0b5bcab65a..048a192522 100644 --- a/examples/walk.js +++ b/examples/walk.js @@ -1,454 +1,454 @@ -// -// walk.js -// version 1.25 -// -// Created by David Wooldridge, June 2015 -// Copyright © 2014 - 2015 High Fidelity, Inc. -// -// Animates an avatar using procedural animation techniques. -// -// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// animations, reach poses, reach pose parameters, transitions, transition parameters, sounds, image/s and reference files -const HIFI_PUBLIC_BUCKET = "https://hifi-public.s3.amazonaws.com/"; -var pathToAssets = HIFI_PUBLIC_BUCKET + "procedural-animator/assets/"; - -Script.include([ - "./libraries/walkConstants.js", - "./libraries/walkFilters.js", - "./libraries/walkApi.js", - pathToAssets + "walkAssets.js" -]); - -// construct Avatar, Motion and (null) Transition -var avatar = new Avatar(); -var motion = new Motion(); -var nullTransition = new Transition(); -motion.currentTransition = nullTransition; - -// create settings (gets initial values from avatar) -Script.include("./libraries/walkSettings.js"); - -// Main loop -Script.update.connect(function(deltaTime) { - - if (motion.isLive) { - - // assess current locomotion state - motion.assess(deltaTime); - - // decide which animation should be playing - selectAnimation(); - - // advance the animation cycle/s by the correct amount/s - advanceAnimations(); - - // update the progress of any live transitions - updateTransitions(); - - // apply translation and rotations - renderMotion(); - - // save this frame's parameters - motion.saveHistory(); - } -}); - -// helper function for selectAnimation() -function setTransition(nextAnimation, playTransitionReachPoses) { - var lastTransition = motion.currentTransition; - var lastAnimation = avatar.currentAnimation; - - // if already transitioning from a blended walk need to maintain the previous walk's direction - if (lastAnimation.lastDirection) { - switch(lastAnimation.lastDirection) { - - case FORWARDS: - lastAnimation = avatar.selectedWalk; - break; - - case BACKWARDS: - lastAnimation = avatar.selectedWalkBackwards; - break; - - case LEFT: - lastAnimation = avatar.selectedSideStepLeft; - break; - - case RIGHT: - lastAnimation = avatar.selectedSideStepRight; - break; - } - } - - motion.currentTransition = new Transition(nextAnimation, lastAnimation, lastTransition, playTransitionReachPoses); - avatar.currentAnimation = nextAnimation; - - // reset default first footstep - if (nextAnimation === avatar.selectedWalkBlend && lastTransition === nullTransition) { - avatar.nextStep = RIGHT; - } -} - -// fly animation blending: smoothing / damping filters -const FLY_BLEND_DAMPING = 50; -var flyUpFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); -var flyDownFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); -var flyForwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); -var flyBackwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); - -// select / blend the appropriate animation for the current state of motion -function selectAnimation() { - var playTransitionReachPoses = true; - - // select appropriate animation. create transitions where appropriate - switch (motion.nextState) { - case STATIC: { - if (avatar.distanceFromSurface < ON_SURFACE_THRESHOLD && - avatar.currentAnimation !== avatar.selectedIdle) { - setTransition(avatar.selectedIdle, playTransitionReachPoses); - } else if (!(avatar.distanceFromSurface < ON_SURFACE_THRESHOLD) && - avatar.currentAnimation !== avatar.selectedHover) { - setTransition(avatar.selectedHover, playTransitionReachPoses); - } - motion.state = STATIC; - avatar.selectedWalkBlend.lastDirection = NONE; - break; - } - - case SURFACE_MOTION: { - // walk transition reach poses are currently only specified for starting to walk forwards - playTransitionReachPoses = (motion.direction === FORWARDS); - var isAlreadyWalking = (avatar.currentAnimation === avatar.selectedWalkBlend); - - switch (motion.direction) { - case FORWARDS: - if (avatar.selectedWalkBlend.lastDirection !== FORWARDS) { - animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend); - avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength; - } - avatar.selectedWalkBlend.lastDirection = FORWARDS; - break; - - case BACKWARDS: - if (avatar.selectedWalkBlend.lastDirection !== BACKWARDS) { - animationOperations.deepCopy(avatar.selectedWalkBackwards, avatar.selectedWalkBlend); - avatar.calibration.strideLength = avatar.selectedWalkBackwards.calibration.strideLength; - } - avatar.selectedWalkBlend.lastDirection = BACKWARDS; - break; - - case LEFT: - animationOperations.deepCopy(avatar.selectedSideStepLeft, avatar.selectedWalkBlend); - avatar.selectedWalkBlend.lastDirection = LEFT; - avatar.calibration.strideLength = avatar.selectedSideStepLeft.calibration.strideLength; - break - - case RIGHT: - animationOperations.deepCopy(avatar.selectedSideStepRight, avatar.selectedWalkBlend); - avatar.selectedWalkBlend.lastDirection = RIGHT; - avatar.calibration.strideLength = avatar.selectedSideStepRight.calibration.strideLength; - break; - - default: - // condition occurs when the avi goes through the floor due to collision hull errors - animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend); - avatar.selectedWalkBlend.lastDirection = FORWARDS; - avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength; - break; - } - - if (!isAlreadyWalking && !motion.isComingToHalt) { - setTransition(avatar.selectedWalkBlend, playTransitionReachPoses); - } - motion.state = SURFACE_MOTION; - break; - } - - case AIR_MOTION: { - // blend the up, down, forward and backward flying animations relative to motion speed and direction - animationOperations.zeroAnimation(avatar.selectedFlyBlend); - - // calculate influences based on velocity and direction - var velocityMagnitude = Vec3.length(motion.velocity); - var verticalProportion = motion.velocity.y / velocityMagnitude; - var thrustProportion = motion.velocity.z / velocityMagnitude / 2; - - // directional components - var upComponent = motion.velocity.y > 0 ? verticalProportion : 0; - var downComponent = motion.velocity.y < 0 ? -verticalProportion : 0; - var forwardComponent = motion.velocity.z < 0 ? -thrustProportion : 0; - var backwardComponent = motion.velocity.z > 0 ? thrustProportion : 0; - - // smooth / damp directional components to add visual 'weight' - upComponent = flyUpFilter.process(upComponent); - downComponent = flyDownFilter.process(downComponent); - forwardComponent = flyForwardFilter.process(forwardComponent); - backwardComponent = flyBackwardFilter.process(backwardComponent); - - // normalise directional components - var normaliser = upComponent + downComponent + forwardComponent + backwardComponent; - upComponent = upComponent / normaliser; - downComponent = downComponent / normaliser; - forwardComponent = forwardComponent / normaliser; - backwardComponent = backwardComponent / normaliser; - - // blend animations proportionally - if (upComponent > 0) { - animationOperations.blendAnimation(avatar.selectedFlyUp, - avatar.selectedFlyBlend, - upComponent); - } - if (downComponent > 0) { - animationOperations.blendAnimation(avatar.selectedFlyDown, - avatar.selectedFlyBlend, - downComponent); - } - if (forwardComponent > 0) { - animationOperations.blendAnimation(avatar.selectedFly, - avatar.selectedFlyBlend, - Math.abs(forwardComponent)); - } - if (backwardComponent > 0) { - animationOperations.blendAnimation(avatar.selectedFlyBackwards, - avatar.selectedFlyBlend, - Math.abs(backwardComponent)); - } - - if (avatar.currentAnimation !== avatar.selectedFlyBlend) { - setTransition(avatar.selectedFlyBlend, playTransitionReachPoses); - } - motion.state = AIR_MOTION; - avatar.selectedWalkBlend.lastDirection = NONE; - break; - } - } // end switch next state of motion -} - -// determine the length of stride. advance the frequency time wheels. advance frequency time wheels for any live transitions -function advanceAnimations() { - var wheelAdvance = 0; - - // turn the frequency time wheel - if (avatar.currentAnimation === avatar.selectedWalkBlend) { - // Using technique described here: http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach - // wrap the stride length around a 'surveyor's wheel' twice and calculate the angular speed at the given (linear) speed - // omega = v / r , where r = circumference / 2 PI and circumference = 2 * stride length - var speed = Vec3.length(motion.velocity); - motion.frequencyTimeWheelRadius = avatar.calibration.strideLength / Math.PI; - var ftWheelAngularVelocity = speed / motion.frequencyTimeWheelRadius; - // calculate the degrees turned (at this angular speed) since last frame - wheelAdvance = filter.radToDeg(motion.deltaTime * ftWheelAngularVelocity); - } else { - // turn the frequency time wheel by the amount specified for this animation - wheelAdvance = filter.radToDeg(avatar.currentAnimation.calibration.frequency * motion.deltaTime); - } - - if (motion.currentTransition !== nullTransition) { - // the last animation is still playing so we turn it's frequency time wheel to maintain the animation - if (motion.currentTransition.lastAnimation === motion.selectedWalkBlend) { - // if at a stop angle (i.e. feet now under the avi) hold the wheel position for remainder of transition - var tolerance = motion.currentTransition.lastFrequencyTimeIncrement + 0.1; - if ((motion.currentTransition.lastFrequencyTimeWheelPos > - (motion.currentTransition.stopAngle - tolerance)) && - (motion.currentTransition.lastFrequencyTimeWheelPos < - (motion.currentTransition.stopAngle + tolerance))) { - motion.currentTransition.lastFrequencyTimeIncrement = 0; - } - } - motion.currentTransition.advancePreviousFrequencyTimeWheel(motion.deltaTime); - } - - // avoid unnaturally fast walking when landing at speed - simulates skimming / skidding - if (Math.abs(wheelAdvance) > MAX_FT_WHEEL_INCREMENT) { - wheelAdvance = 0; - } - - // advance the walk wheel the appropriate amount - motion.advanceFrequencyTimeWheel(wheelAdvance); - - // walking? then see if it's a good time to measure the stride length (needs to be at least 97% of max walking speed) - const ALMOST_ONE = 0.97; - if (avatar.currentAnimation === avatar.selectedWalkBlend && - (Vec3.length(motion.velocity) / MAX_WALK_SPEED > ALMOST_ONE)) { - - var strideMaxAt = avatar.currentAnimation.calibration.strideMaxAt; - const TOLERANCE = 1.0; - - if (motion.frequencyTimeWheelPos < (strideMaxAt + TOLERANCE) && - motion.frequencyTimeWheelPos > (strideMaxAt - TOLERANCE) && - motion.currentTransition === nullTransition) { - // measure and save stride length - var footRPos = MyAvatar.getJointPosition("RightFoot"); - var footLPos = MyAvatar.getJointPosition("LeftFoot"); - avatar.calibration.strideLength = Vec3.distance(footRPos, footLPos); - avatar.currentAnimation.calibration.strideLength = avatar.calibration.strideLength; - } else { - // use the previously saved value for stride length - avatar.calibration.strideLength = avatar.currentAnimation.calibration.strideLength; - } - } // end get walk stride length -} - -// initialise a new transition. update progress of a live transition -function updateTransitions() { - - if (motion.currentTransition !== nullTransition) { - // is this a new transition? - if (motion.currentTransition.progress === 0) { - // do we have overlapping transitions? - if (motion.currentTransition.lastTransition !== nullTransition) { - // is the last animation for the nested transition the same as the new animation? - if (motion.currentTransition.lastTransition.lastAnimation === avatar.currentAnimation) { - // then sync the nested transition's frequency time wheel for a smooth animation blend - motion.frequencyTimeWheelPos = motion.currentTransition.lastTransition.lastFrequencyTimeWheelPos; - } - } - } - if (motion.currentTransition.updateProgress() === TRANSITION_COMPLETE) { - motion.currentTransition = nullTransition; - } - } -} - -// helper function for renderMotion(). calculate the amount to lean forwards (or backwards) based on the avi's velocity -var leanPitchSmoothingFilter = filter.createButterworthFilter(); -function getLeanPitch() { - var leanProgress = 0; - - if (motion.direction === DOWN || - motion.direction === FORWARDS || - motion.direction === BACKWARDS) { - leanProgress = -motion.velocity.z / TOP_SPEED; - } - // use filters to shape the walking acceleration response - leanProgress = leanPitchSmoothingFilter.process(leanProgress); - return PITCH_MAX * leanProgress; -} - -// helper function for renderMotion(). calculate the angle at which to bank into corners whilst turning -var leanRollSmoothingFilter = filter.createButterworthFilter(); -function getLeanRoll() { - var leanRollProgress = 0; - var linearContribution = 0; - const LOG_SCALER = 8; - - if (Vec3.length(motion.velocity) > 0) { - linearContribution = (Math.log(Vec3.length(motion.velocity) / TOP_SPEED) + LOG_SCALER) / LOG_SCALER; - } - var angularContribution = Math.abs(motion.yawDelta) / DELTA_YAW_MAX; - leanRollProgress = linearContribution; - leanRollProgress *= angularContribution; - // shape the response curve - leanRollProgress = filter.bezier(leanRollProgress, {x: 1, y: 0}, {x: 1, y: 0}); - // which way to lean? - var turnSign = (motion.yawDelta >= 0) ? 1 : -1; - - if (motion.direction === BACKWARDS || - motion.direction === LEFT) { - turnSign *= -1; - } - // filter progress - leanRollProgress = leanRollSmoothingFilter.process(turnSign * leanRollProgress); - return ROLL_MAX * leanRollProgress; -} - -// animate the avatar using sine waves, geometric waveforms and harmonic generators -function renderMotion() { - // leaning in response to speed and acceleration - var leanPitch = motion.state === STATIC ? 0 : getLeanPitch(); - var leanRoll = motion.state === STATIC ? 0 : getLeanRoll(); - var lastDirection = motion.lastDirection; - // hips translations from currently playing animations - var hipsTranslations = {x:0, y:0, z:0}; - - if (motion.currentTransition !== nullTransition) { - // maintain previous direction when transitioning from a walk - if (motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) { - motion.lastDirection = motion.currentTransition.lastDirection; - } - hipsTranslations = motion.currentTransition.blendTranslations(motion.frequencyTimeWheelPos, - motion.lastDirection); - } else { - hipsTranslations = animationOperations.calculateTranslations(avatar.currentAnimation, - motion.frequencyTimeWheelPos, - motion.direction); - } - // factor any leaning into the hips offset - hipsTranslations.z += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanPitch)); - hipsTranslations.x += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanRoll)); - - // ensure skeleton offsets are within the 1m limit - hipsTranslations.x = hipsTranslations.x > 1 ? 1 : hipsTranslations.x; - hipsTranslations.x = hipsTranslations.x < -1 ? -1 : hipsTranslations.x; - hipsTranslations.y = hipsTranslations.y > 1 ? 1 : hipsTranslations.y; - hipsTranslations.y = hipsTranslations.y < -1 ? -1 : hipsTranslations.y; - hipsTranslations.z = hipsTranslations.z > 1 ? 1 : hipsTranslations.z; - hipsTranslations.z = hipsTranslations.z < -1 ? -1 : hipsTranslations.z; - // apply translations - MyAvatar.setSkeletonOffset(hipsTranslations); - - // play footfall sound? - var producingFootstepSounds = (avatar.currentAnimation === avatar.selectedWalkBlend) && avatar.makesFootStepSounds; - - if (motion.currentTransition !== nullTransition && avatar.makesFootStepSounds) { - if (motion.currentTransition.nextAnimation === avatar.selectedWalkBlend || - motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) { - producingFootstepSounds = true; - } - } - if (producingFootstepSounds) { - const QUARTER_CYCLE = 90; - const THREE_QUARTER_CYCLE = 270; - var ftWheelPosition = motion.frequencyTimeWheelPos; - - if (motion.currentTransition !== nullTransition && - motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) { - ftWheelPosition = motion.currentTransition.lastFrequencyTimeWheelPos; - } - if (avatar.nextStep === LEFT && ftWheelPosition > THREE_QUARTER_CYCLE) { - avatar.makeFootStepSound(); - } else if (avatar.nextStep === RIGHT && (ftWheelPosition < THREE_QUARTER_CYCLE && ftWheelPosition > QUARTER_CYCLE)) { - avatar.makeFootStepSound(); - } - } - - // apply joint rotations - for (jointName in avatar.currentAnimation.joints) { - var joint = walkAssets.animationReference.joints[jointName]; - var jointRotations = undefined; - - // ignore arms / head rotations if options are selected in the settings - if (avatar.armsFree && (joint.IKChain === "LeftArm" || joint.IKChain === "RightArm")) { - continue; - } - if (avatar.headFree && joint.IKChain === "Head") { - continue; - } - - // if there's a live transition, blend the rotations with the last animation's rotations - if (motion.currentTransition !== nullTransition) { - jointRotations = motion.currentTransition.blendRotations(jointName, - motion.frequencyTimeWheelPos, - motion.lastDirection); - } else { - jointRotations = animationOperations.calculateRotations(jointName, - avatar.currentAnimation, - motion.frequencyTimeWheelPos, - motion.direction); - } - - // apply angular velocity and speed induced leaning - if (jointName === "Hips") { - jointRotations.x += leanPitch; - jointRotations.z += leanRoll; - } - - // apply rotations - MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations)); - } -} \ No newline at end of file +// +// walk.js +// version 1.25 +// +// Created by David Wooldridge, June 2015 +// Copyright © 2014 - 2015 High Fidelity, Inc. +// +// Animates an avatar using procedural animation techniques. +// +// Editing tools for animation data files available here: https://github.com/DaveDubUK/walkTools +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// animations, reach poses, reach pose parameters, transitions, transition parameters, sounds, image/s and reference files +const HIFI_PUBLIC_BUCKET = "https://hifi-public.s3.amazonaws.com/"; +var pathToAssets = HIFI_PUBLIC_BUCKET + "procedural-animator/assets/"; + +Script.include([ + "./libraries/walkConstants.js", + "./libraries/walkFilters.js", + "./libraries/walkApi.js", + pathToAssets + "walkAssets.js" +]); + +// construct Avatar, Motion and (null) Transition +var avatar = new Avatar(); +var motion = new Motion(); +var nullTransition = new Transition(); +motion.currentTransition = nullTransition; + +// create settings (gets initial values from avatar) +Script.include("./libraries/walkSettings.js"); + +// Main loop +Script.update.connect(function(deltaTime) { + + if (motion.isLive) { + + // assess current locomotion state + motion.assess(deltaTime); + + // decide which animation should be playing + selectAnimation(); + + // advance the animation cycle/s by the correct amount/s + advanceAnimations(); + + // update the progress of any live transitions + updateTransitions(); + + // apply translation and rotations + renderMotion(); + + // save this frame's parameters + motion.saveHistory(); + } +}); + +// helper function for selectAnimation() +function setTransition(nextAnimation, playTransitionReachPoses) { + var lastTransition = motion.currentTransition; + var lastAnimation = avatar.currentAnimation; + + // if already transitioning from a blended walk need to maintain the previous walk's direction + if (lastAnimation.lastDirection) { + switch(lastAnimation.lastDirection) { + + case FORWARDS: + lastAnimation = avatar.selectedWalk; + break; + + case BACKWARDS: + lastAnimation = avatar.selectedWalkBackwards; + break; + + case LEFT: + lastAnimation = avatar.selectedSideStepLeft; + break; + + case RIGHT: + lastAnimation = avatar.selectedSideStepRight; + break; + } + } + + motion.currentTransition = new Transition(nextAnimation, lastAnimation, lastTransition, playTransitionReachPoses); + avatar.currentAnimation = nextAnimation; + + // reset default first footstep + if (nextAnimation === avatar.selectedWalkBlend && lastTransition === nullTransition) { + avatar.nextStep = RIGHT; + } +} + +// fly animation blending: smoothing / damping filters +const FLY_BLEND_DAMPING = 50; +var flyUpFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); +var flyDownFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); +var flyForwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); +var flyBackwardFilter = filter.createAveragingFilter(FLY_BLEND_DAMPING); + +// select / blend the appropriate animation for the current state of motion +function selectAnimation() { + var playTransitionReachPoses = true; + + // select appropriate animation. create transitions where appropriate + switch (motion.nextState) { + case STATIC: { + if (avatar.distanceFromSurface < ON_SURFACE_THRESHOLD && + avatar.currentAnimation !== avatar.selectedIdle) { + setTransition(avatar.selectedIdle, playTransitionReachPoses); + } else if (!(avatar.distanceFromSurface < ON_SURFACE_THRESHOLD) && + avatar.currentAnimation !== avatar.selectedHover) { + setTransition(avatar.selectedHover, playTransitionReachPoses); + } + motion.state = STATIC; + avatar.selectedWalkBlend.lastDirection = NONE; + break; + } + + case SURFACE_MOTION: { + // walk transition reach poses are currently only specified for starting to walk forwards + playTransitionReachPoses = (motion.direction === FORWARDS); + var isAlreadyWalking = (avatar.currentAnimation === avatar.selectedWalkBlend); + + switch (motion.direction) { + case FORWARDS: + if (avatar.selectedWalkBlend.lastDirection !== FORWARDS) { + animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend); + avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength; + } + avatar.selectedWalkBlend.lastDirection = FORWARDS; + break; + + case BACKWARDS: + if (avatar.selectedWalkBlend.lastDirection !== BACKWARDS) { + animationOperations.deepCopy(avatar.selectedWalkBackwards, avatar.selectedWalkBlend); + avatar.calibration.strideLength = avatar.selectedWalkBackwards.calibration.strideLength; + } + avatar.selectedWalkBlend.lastDirection = BACKWARDS; + break; + + case LEFT: + animationOperations.deepCopy(avatar.selectedSideStepLeft, avatar.selectedWalkBlend); + avatar.selectedWalkBlend.lastDirection = LEFT; + avatar.calibration.strideLength = avatar.selectedSideStepLeft.calibration.strideLength; + break + + case RIGHT: + animationOperations.deepCopy(avatar.selectedSideStepRight, avatar.selectedWalkBlend); + avatar.selectedWalkBlend.lastDirection = RIGHT; + avatar.calibration.strideLength = avatar.selectedSideStepRight.calibration.strideLength; + break; + + default: + // condition occurs when the avi goes through the floor due to collision hull errors + animationOperations.deepCopy(avatar.selectedWalk, avatar.selectedWalkBlend); + avatar.selectedWalkBlend.lastDirection = FORWARDS; + avatar.calibration.strideLength = avatar.selectedWalk.calibration.strideLength; + break; + } + + if (!isAlreadyWalking && !motion.isComingToHalt) { + setTransition(avatar.selectedWalkBlend, playTransitionReachPoses); + } + motion.state = SURFACE_MOTION; + break; + } + + case AIR_MOTION: { + // blend the up, down, forward and backward flying animations relative to motion speed and direction + animationOperations.zeroAnimation(avatar.selectedFlyBlend); + + // calculate influences based on velocity and direction + var velocityMagnitude = Vec3.length(motion.velocity); + var verticalProportion = motion.velocity.y / velocityMagnitude; + var thrustProportion = motion.velocity.z / velocityMagnitude / 2; + + // directional components + var upComponent = motion.velocity.y > 0 ? verticalProportion : 0; + var downComponent = motion.velocity.y < 0 ? -verticalProportion : 0; + var forwardComponent = motion.velocity.z < 0 ? -thrustProportion : 0; + var backwardComponent = motion.velocity.z > 0 ? thrustProportion : 0; + + // smooth / damp directional components to add visual 'weight' + upComponent = flyUpFilter.process(upComponent); + downComponent = flyDownFilter.process(downComponent); + forwardComponent = flyForwardFilter.process(forwardComponent); + backwardComponent = flyBackwardFilter.process(backwardComponent); + + // normalise directional components + var normaliser = upComponent + downComponent + forwardComponent + backwardComponent; + upComponent = upComponent / normaliser; + downComponent = downComponent / normaliser; + forwardComponent = forwardComponent / normaliser; + backwardComponent = backwardComponent / normaliser; + + // blend animations proportionally + if (upComponent > 0) { + animationOperations.blendAnimation(avatar.selectedFlyUp, + avatar.selectedFlyBlend, + upComponent); + } + if (downComponent > 0) { + animationOperations.blendAnimation(avatar.selectedFlyDown, + avatar.selectedFlyBlend, + downComponent); + } + if (forwardComponent > 0) { + animationOperations.blendAnimation(avatar.selectedFly, + avatar.selectedFlyBlend, + Math.abs(forwardComponent)); + } + if (backwardComponent > 0) { + animationOperations.blendAnimation(avatar.selectedFlyBackwards, + avatar.selectedFlyBlend, + Math.abs(backwardComponent)); + } + + if (avatar.currentAnimation !== avatar.selectedFlyBlend) { + setTransition(avatar.selectedFlyBlend, playTransitionReachPoses); + } + motion.state = AIR_MOTION; + avatar.selectedWalkBlend.lastDirection = NONE; + break; + } + } // end switch next state of motion +} + +// determine the length of stride. advance the frequency time wheels. advance frequency time wheels for any live transitions +function advanceAnimations() { + var wheelAdvance = 0; + + // turn the frequency time wheel + if (avatar.currentAnimation === avatar.selectedWalkBlend) { + // Using technique described here: http://www.gdcvault.com/play/1020583/Animation-Bootcamp-An-Indie-Approach + // wrap the stride length around a 'surveyor's wheel' twice and calculate the angular speed at the given (linear) speed + // omega = v / r , where r = circumference / 2 PI and circumference = 2 * stride length + var speed = Vec3.length(motion.velocity); + motion.frequencyTimeWheelRadius = avatar.calibration.strideLength / Math.PI; + var ftWheelAngularVelocity = speed / motion.frequencyTimeWheelRadius; + // calculate the degrees turned (at this angular speed) since last frame + wheelAdvance = filter.radToDeg(motion.deltaTime * ftWheelAngularVelocity); + } else { + // turn the frequency time wheel by the amount specified for this animation + wheelAdvance = filter.radToDeg(avatar.currentAnimation.calibration.frequency * motion.deltaTime); + } + + if (motion.currentTransition !== nullTransition) { + // the last animation is still playing so we turn it's frequency time wheel to maintain the animation + if (motion.currentTransition.lastAnimation === motion.selectedWalkBlend) { + // if at a stop angle (i.e. feet now under the avi) hold the wheel position for remainder of transition + var tolerance = motion.currentTransition.lastFrequencyTimeIncrement + 0.1; + if ((motion.currentTransition.lastFrequencyTimeWheelPos > + (motion.currentTransition.stopAngle - tolerance)) && + (motion.currentTransition.lastFrequencyTimeWheelPos < + (motion.currentTransition.stopAngle + tolerance))) { + motion.currentTransition.lastFrequencyTimeIncrement = 0; + } + } + motion.currentTransition.advancePreviousFrequencyTimeWheel(motion.deltaTime); + } + + // avoid unnaturally fast walking when landing at speed - simulates skimming / skidding + if (Math.abs(wheelAdvance) > MAX_FT_WHEEL_INCREMENT) { + wheelAdvance = 0; + } + + // advance the walk wheel the appropriate amount + motion.advanceFrequencyTimeWheel(wheelAdvance); + + // walking? then see if it's a good time to measure the stride length (needs to be at least 97% of max walking speed) + const ALMOST_ONE = 0.97; + if (avatar.currentAnimation === avatar.selectedWalkBlend && + (Vec3.length(motion.velocity) / MAX_WALK_SPEED > ALMOST_ONE)) { + + var strideMaxAt = avatar.currentAnimation.calibration.strideMaxAt; + const TOLERANCE = 1.0; + + if (motion.frequencyTimeWheelPos < (strideMaxAt + TOLERANCE) && + motion.frequencyTimeWheelPos > (strideMaxAt - TOLERANCE) && + motion.currentTransition === nullTransition) { + // measure and save stride length + var footRPos = MyAvatar.getJointPosition("RightFoot"); + var footLPos = MyAvatar.getJointPosition("LeftFoot"); + avatar.calibration.strideLength = Vec3.distance(footRPos, footLPos); + avatar.currentAnimation.calibration.strideLength = avatar.calibration.strideLength; + } else { + // use the previously saved value for stride length + avatar.calibration.strideLength = avatar.currentAnimation.calibration.strideLength; + } + } // end get walk stride length +} + +// initialise a new transition. update progress of a live transition +function updateTransitions() { + + if (motion.currentTransition !== nullTransition) { + // is this a new transition? + if (motion.currentTransition.progress === 0) { + // do we have overlapping transitions? + if (motion.currentTransition.lastTransition !== nullTransition) { + // is the last animation for the nested transition the same as the new animation? + if (motion.currentTransition.lastTransition.lastAnimation === avatar.currentAnimation) { + // then sync the nested transition's frequency time wheel for a smooth animation blend + motion.frequencyTimeWheelPos = motion.currentTransition.lastTransition.lastFrequencyTimeWheelPos; + } + } + } + if (motion.currentTransition.updateProgress() === TRANSITION_COMPLETE) { + motion.currentTransition = nullTransition; + } + } +} + +// helper function for renderMotion(). calculate the amount to lean forwards (or backwards) based on the avi's velocity +var leanPitchSmoothingFilter = filter.createButterworthFilter(); +function getLeanPitch() { + var leanProgress = 0; + + if (motion.direction === DOWN || + motion.direction === FORWARDS || + motion.direction === BACKWARDS) { + leanProgress = -motion.velocity.z / TOP_SPEED; + } + // use filters to shape the walking acceleration response + leanProgress = leanPitchSmoothingFilter.process(leanProgress); + return PITCH_MAX * leanProgress; +} + +// helper function for renderMotion(). calculate the angle at which to bank into corners whilst turning +var leanRollSmoothingFilter = filter.createButterworthFilter(); +function getLeanRoll() { + var leanRollProgress = 0; + var linearContribution = 0; + const LOG_SCALER = 8; + + if (Vec3.length(motion.velocity) > 0) { + linearContribution = (Math.log(Vec3.length(motion.velocity) / TOP_SPEED) + LOG_SCALER) / LOG_SCALER; + } + var angularContribution = Math.abs(motion.yawDelta) / DELTA_YAW_MAX; + leanRollProgress = linearContribution; + leanRollProgress *= angularContribution; + // shape the response curve + leanRollProgress = filter.bezier(leanRollProgress, {x: 1, y: 0}, {x: 1, y: 0}); + // which way to lean? + var turnSign = (motion.yawDelta >= 0) ? 1 : -1; + + if (motion.direction === BACKWARDS || + motion.direction === LEFT) { + turnSign *= -1; + } + // filter progress + leanRollProgress = leanRollSmoothingFilter.process(turnSign * leanRollProgress); + return ROLL_MAX * leanRollProgress; +} + +// animate the avatar using sine waves, geometric waveforms and harmonic generators +function renderMotion() { + // leaning in response to speed and acceleration + var leanPitch = motion.state === STATIC ? 0 : getLeanPitch(); + var leanRoll = motion.state === STATIC ? 0 : getLeanRoll(); + var lastDirection = motion.lastDirection; + // hips translations from currently playing animations + var hipsTranslations = {x:0, y:0, z:0}; + + if (motion.currentTransition !== nullTransition) { + // maintain previous direction when transitioning from a walk + if (motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) { + motion.lastDirection = motion.currentTransition.lastDirection; + } + hipsTranslations = motion.currentTransition.blendTranslations(motion.frequencyTimeWheelPos, + motion.lastDirection); + } else { + hipsTranslations = animationOperations.calculateTranslations(avatar.currentAnimation, + motion.frequencyTimeWheelPos, + motion.direction); + } + // factor any leaning into the hips offset + hipsTranslations.z += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanPitch)); + hipsTranslations.x += avatar.calibration.hipsToFeet * Math.sin(filter.degToRad(leanRoll)); + + // ensure skeleton offsets are within the 1m limit + hipsTranslations.x = hipsTranslations.x > 1 ? 1 : hipsTranslations.x; + hipsTranslations.x = hipsTranslations.x < -1 ? -1 : hipsTranslations.x; + hipsTranslations.y = hipsTranslations.y > 1 ? 1 : hipsTranslations.y; + hipsTranslations.y = hipsTranslations.y < -1 ? -1 : hipsTranslations.y; + hipsTranslations.z = hipsTranslations.z > 1 ? 1 : hipsTranslations.z; + hipsTranslations.z = hipsTranslations.z < -1 ? -1 : hipsTranslations.z; + // apply translations + MyAvatar.setSkeletonOffset(hipsTranslations); + + // play footfall sound? + var producingFootstepSounds = (avatar.currentAnimation === avatar.selectedWalkBlend) && avatar.makesFootStepSounds; + + if (motion.currentTransition !== nullTransition && avatar.makesFootStepSounds) { + if (motion.currentTransition.nextAnimation === avatar.selectedWalkBlend || + motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) { + producingFootstepSounds = true; + } + } + if (producingFootstepSounds) { + const QUARTER_CYCLE = 90; + const THREE_QUARTER_CYCLE = 270; + var ftWheelPosition = motion.frequencyTimeWheelPos; + + if (motion.currentTransition !== nullTransition && + motion.currentTransition.lastAnimation === avatar.selectedWalkBlend) { + ftWheelPosition = motion.currentTransition.lastFrequencyTimeWheelPos; + } + if (avatar.nextStep === LEFT && ftWheelPosition > THREE_QUARTER_CYCLE) { + avatar.makeFootStepSound(); + } else if (avatar.nextStep === RIGHT && (ftWheelPosition < THREE_QUARTER_CYCLE && ftWheelPosition > QUARTER_CYCLE)) { + avatar.makeFootStepSound(); + } + } + + // apply joint rotations + for (jointName in avatar.currentAnimation.joints) { + var joint = walkAssets.animationReference.joints[jointName]; + var jointRotations = undefined; + + // ignore arms / head rotations if options are selected in the settings + if (avatar.armsFree && (joint.IKChain === "LeftArm" || joint.IKChain === "RightArm")) { + continue; + } + if (avatar.headFree && joint.IKChain === "Head") { + continue; + } + + // if there's a live transition, blend the rotations with the last animation's rotations + if (motion.currentTransition !== nullTransition) { + jointRotations = motion.currentTransition.blendRotations(jointName, + motion.frequencyTimeWheelPos, + motion.lastDirection); + } else { + jointRotations = animationOperations.calculateRotations(jointName, + avatar.currentAnimation, + motion.frequencyTimeWheelPos, + motion.direction); + } + + // apply angular velocity and speed induced leaning + if (jointName === "Hips") { + jointRotations.x += leanPitch; + jointRotations.z += leanRoll; + } + + // apply rotations + MyAvatar.setJointRotation(jointName, Quat.fromVec3Degrees(jointRotations)); + } +} From d02fe784a95771fbfa98449f40eb3bcbc1652eb8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 20 Oct 2015 13:58:44 -0700 Subject: [PATCH 0275/1003] css and cfg filetypes are text in .gitattributes --- .gitattributes | 28 +- examples/html/walkStyle.css | 96 ++-- interface/resources/visage/tracker.cfg | 668 ++++++++++++------------- 3 files changed, 397 insertions(+), 395 deletions(-) diff --git a/.gitattributes b/.gitattributes index bf357796a4..4efc7667f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,25 +1,27 @@ +*.cfg text *.cpp text +*.css text *.c text -*.h text -*.qml text -*.js text -*.json text -*.slv text -*.slf text -*.slh text -*.vert text *.frag text *.fst text -*.ini text +*.h text *.html text +*.ini text +*.json text +*.js text +*.qml text +*.slf text +*.slh text +*.slv text *.ts text *.txt text +*.vert text # Denote all files that are truly binary and should not be modified. -*.png binary -*.jpg binary -*.wav binary -*.fbx binary *.dds binary +*.fbx binary +*.jpg binary +*.png binary *.svg binary *.ttf binary +*.wav binary diff --git a/examples/html/walkStyle.css b/examples/html/walkStyle.css index 22f465411a..b59f7c63f0 100644 --- a/examples/html/walkStyle.css +++ b/examples/html/walkStyle.css @@ -1,48 +1,48 @@ -* { -} - -body { - margin: 0; - padding: 0; - - background-color: rgb(76, 76, 76); - color: rgb(204, 204, 204); - font-family: Arial; - font-size: 9pt; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#walk-settings-header { - padding: 0.5em; -} - -.settings-section { - display: block; - margin: 10 10; - height: 22.5pt; -} - -.settings-section label { - font-weight: bold; -} - -.settings-section span { - float: right; -} - -input[type=button] { - cursor: pointer; - background-color: #608e96; - border-color: #608e96; - border-radius: 3.75pt; - padding: 3.75pt 7.5pt; - border: 0; - color: #fff; - font-weight: bold; -} +* { +} + +body { + margin: 0; + padding: 0; + + background-color: rgb(76, 76, 76); + color: rgb(204, 204, 204); + font-family: Arial; + font-size: 9pt; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#walk-settings-header { + padding: 0.5em; +} + +.settings-section { + display: block; + margin: 10 10; + height: 22.5pt; +} + +.settings-section label { + font-weight: bold; +} + +.settings-section span { + float: right; +} + +input[type=button] { + cursor: pointer; + background-color: #608e96; + border-color: #608e96; + border-radius: 3.75pt; + padding: 3.75pt 7.5pt; + border: 0; + color: #fff; + font-weight: bold; +} diff --git a/interface/resources/visage/tracker.cfg b/interface/resources/visage/tracker.cfg index 2efb7f3463..7eb4198654 100644 --- a/interface/resources/visage/tracker.cfg +++ b/interface/resources/visage/tracker.cfg @@ -1,334 +1,334 @@ -display_width 800 - -max_init_image_width 800 -max_work_image_width 384 -fast_image_resize 1 - -camera_input 0 -camera_device 0 -camera_width 800 -camera_height 600 -camera_frame_rate 30 -camera_mirror 1 -camera_settings_dialog 0 -camera_auto_settings 0 - -video_file_sync 1 - -automatic_initialisation 1 -init_yaw_threshold 0.005 -init_roll_threshold 10 -init_velocity_threshold 0.015 -init_timeout 1000 -init_timeout_enable 6 -init_display_status 1 -recovery_timeout 800 - -display_video 1 -display_model_texture 0 -display_tri_mask 0 -display_model_wireframe 0 -display_results 1 -display_track_points 0 - -detect_strip_area_threshold 1000 -detect_strip_angle_threshold 0.15 -detect_strip_ratio_threshold 0.2 -detect_strip_perfect_ratio 6.848 -detect_strip_roi_y_offset 0 -detect_strip_roi_width 2 -detect_strip_roi_height 4 - -smoothing_factors - 5 25 -2 100 -1 50 25 0 -#translation rotation action_units eyebrows mouth gaze eye_closure other - -process_eyes 1 -leye_closing_au 12 -reye_closing_au 12 -eye_h_rotation_au 15 -eye_v_rotation_au 16 -eye_points_coords -162 1.0 0.0 0.0 -157 0.0 0.0 1.0 - -bdts_points_use - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -#3.5 3.6 3.7 3.8 3.11 3.12 8.1 8.2 8.3 8.4 9.1 9.2 9.3 4.1 4.2 4.3 4.4 4.5 4.6 2.1 - -bdts_points_angle - 0 0 20 -20 20 -20 0 0 20 -20 20 -20 0 25 -25 20 -20 20 -20 0 0 - -model_filename candide3-exp.wfm -fdp_filename candide3.fdp -bdts_data_path bdtsdata - -au_use - 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -#AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 - -au_names -au_nose_wrinkler -au_jaw_z_push -au_jaw_x_push -au_jaw_drop -au_lower_lip_drop -au_upper_lip_raiser -au_lip_stretcher_left -au_lip_corner_depressor -au_lip_presser -au_left_outer_brow_raiser -au_left_inner_brow_raiser -au_left_brow_lowerer -au_leye_closed -au_lid_tightener -au_upper_lid_raiser -au_rotate_eyes_left -au_rotate_eyes_down -au_lower_lip_x_push -au_lip_stretcher_right -au_right_outer_brow_raiser -au_right_inner_brow_raiser -au_right_brow_lowerer -au_reye_closed - - -ekf_sensitivity - 100 100 100 100 100 100 100 100 100 100 100 200 200 100 100 400 400 100 100 100 100 300 300 100 100 400 400 100 100 -#x y z rx ry rz AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 - -au_gravity - 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -#AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 - -gravity_threshold 0.0 - -camera_focus 3 - -tex_size 512 - -ROI_num 14 - -#ROI 0 - nose region (setting default values) -feature_points_num[0] 7 -feature_points_tri_use_num[0] 21 -feature_points_tri_use_list[0] -48 154 54 55 152 45 46 47 103 109 -108 107 106 101 104 110 111 105 99 102 -100 -bound_rect_modifiers[0] -10 -10 -20 10 -feature_points_min_distance[0] 0.78125 -feature_point_block_size[0] 0.46875 -search_range[0] 4.0625 4.0625 -refine_range[0] 0.3125 0.3125 -patch_size[0] 4.6875 -match_method[0] 5 -bad_match_threshold[0] 0.35 -sensitivity_falloff[0] 10 -init_angle[0] 0 -feature_points_coords[0] -97 0.18 0.54 0.28 -90 0.18 0.28 0.54 -51 0.76 0.09 0.15 -97 0.36 0.10 0.54 -90 0.36 0.54 0.10 -69 0.55 0.10 0.35 -67 0.55 0.35 0.10 - -#ROI 1 - left eyebrow region -feature_points_num[1] 4 -feature_points_tri_use_num[1] 2 -feature_points_tri_use_list[1] -25 26 -bound_rect_modifiers[1] 2 2 40 2 -feature_points_coords[1] -25 0.0 0.0 1.0 -26 0.15 0.1 0.75 -25 1.0 0.0 0.0 -26 0.2 0.4 0.4 - -#ROI 2 - right eyebrow region -feature_points_num[2] 4 -feature_points_tri_use_num[2] 2 -feature_points_tri_use_list[2] -33 34 -bound_rect_modifiers[2] 2 2 40 2 -feature_points_coords[2] -33 0.15 0.75 0.1 -34 1.0 0.0 0.0 -33 0.0 0.0 1.0 -33 0.2 0.4 0.4 - -#ROI 3 - left outer eye corner region -feature_points_num[3] 3 -feature_points_tri_use_num[3] 4 -feature_points_tri_use_list[3] -112 23 39 115 -bound_rect_modifiers[3] -5 -5 -25 -25 -feature_points_coords[3] -23 0 0.2 0.8 -119 0.8351877 0.08414026 0.08067206 -116 0.3218788 0.4626164 0.2155048 - -#ROI 4 - right outer eye corner region -feature_points_num[4] 3 -feature_points_tri_use_num[4] 4 -feature_points_tri_use_list[4] -142 36 42 145 -bound_rect_modifiers[4] -5 -5 -25 -25 -feature_points_coords[4] -36 0 0.8 0.2 -151 0.8566859 0.0405132 0.1028009 -146 0.2871178 0.3467231 0.366159 - -#ROI 5 - left inner eye corner region -feature_points_num[5] 3 -feature_points_tri_use_num[5] 5 -feature_points_tri_use_list[5] -122 28 99 100 125 -bound_rect_modifiers[5] -5 -5 -25 -40 -feature_points_coords[5] -99 0.2 0.1 0.7 -122 0.2664618 0.3707059 0.3628323 -125 0.387767 0.4655813 0.1466517 - -#ROI 6 - right inner eye corner region -feature_points_num[6] 3 -feature_points_tri_use_num[6] 5 -feature_points_tri_use_list[6] -132 31 102 104 135 -bound_rect_modifiers[6] -5 -5 -25 -40 -feature_points_coords[6] -102 0.2 0.7 0.1 -132 0.3673472 0.2426805 0.3899724 -135 0.2833096 0.1345753 0.5821151 - -#ROI 7 - lower lip region -feature_points_num[7] 6 -feature_points_tri_use_num[7] 6 -feature_points_tri_use_list[7] -79 80 82 84 52 53 -bound_rect_modifiers[7] 0 0 0 -80 -refine_range[7] 0.5 1 -patch_size[7] 7 -feature_points_coords[7] -80 0.0 0.0 1.0 -80 1.0 0.0 0.0 -80 0.0 1.0 0.0 -79 0.0 0.0 1.0 -82 0.1 0.3 0.6 -84 0.1 0.6 0.3 - -#ROI 8 - upper lip region -feature_points_num[8] 6 -feature_points_tri_use_num[8] 10 -feature_points_tri_use_list[8] -69 49 155 51 50 153 44 67 57 60 -bound_rect_modifiers[8] 0 0 -70 0 -refine_range[8] 0.5 1 -patch_size[8] 7 -feature_points_coords[8] -67 0.1 0.45 0.45 -69 0.1 0.45 0.45 -57 0.0 0.0 1.0 -60 0.0 1.0 0.0 -51 0.0 1.0 0.0 -51 0.3 0.7 0.0 - -#ROI 9 - left lip corner region -feature_points_num[9] 4 -feature_points_tri_use_num[9] 4 -feature_points_tri_use_list[9] -87 89 81 68 -bound_rect_modifiers[9] 0 0 -35 -35 -refine_range[9] 1 1 -patch_size[9] 7 -feature_points_coords[9] -87 0.0 0.0 1.0 -87 0.2 0.0 0.8 -66 0.33 0.33 0.34 -72 0.33 0.33 0.34 - -#ROI 10 - right lip corner region -feature_points_num[10] 4 -feature_points_tri_use_num[10] 4 -feature_points_tri_use_list[10] -94 96 83 70 -bound_rect_modifiers[10] 0 0 -35 -35 -refine_range[10] 1 1 -patch_size[10] 7 -feature_points_coords[10] -94 0.0 1.0 0.0 -94 0.2 0.8 0.0 -64 0.33 0.33 0.34 -78 0.33 0.33 0.34 - -#ROI 11 - jaw region -feature_points_num[11] 4 -feature_points_tri_use_num[11] 2 -feature_points_tri_use_list[11] -52 53 -bound_rect_modifiers[11] 0 0 -30 0 -refine_range[11] 0.5 1 -patch_size[11] 7 -bad_match_threshold[11] 0.6 -feature_points_coords[11] -52 0.4 0.3 0.3 -53 0.4 0.3 0.3 -87 0.0 0.5 0.5 -94 0.0 0.5 0.5 - -feature_points_num[12] 6 -feature_points_tri_use_num[12] 3 -feature_points_tri_use_list[12] -188 189 190 191 -bound_rect_modifiers[12] -30 0 0 -30 -init_angle[12] -20 - -feature_points_num[13] 6 -feature_points_tri_use_num[13] 3 -feature_points_tri_use_list[13] -192 193 194 195 -bound_rect_modifiers[13] 0 -30 0 -30 -init_angle[13] 20 - -recovery_frames 4 -global_bad_match_threshold 0.5 -visibility_check 1 - -rotation_limit --1.570796 1.570796 - 1.570796 4.712389 --3.2 3.2 - -translation_limit --4 4 --3 3 - 0 11 - -action_unit_limit --0.5 0.5 --0.5 0.5 --1.0 1.0 --0.05 1.2 --0.05 1.5 --0.05 1.5 --1.0 1.0 --1.5 1.5 --1.5 1.5 --1.5 2 --1.5 2 --1 1.5 --2.8 2.8 --1.5 1.5 --1.5 1.5 --100 100 --100 100 --1.5 1.5 --1.5 1.5 --1.5 1.5 --1.5 1.5 --1.5 1.5 --2.8 2.8 - - +display_width 800 + +max_init_image_width 800 +max_work_image_width 384 +fast_image_resize 1 + +camera_input 0 +camera_device 0 +camera_width 800 +camera_height 600 +camera_frame_rate 30 +camera_mirror 1 +camera_settings_dialog 0 +camera_auto_settings 0 + +video_file_sync 1 + +automatic_initialisation 1 +init_yaw_threshold 0.005 +init_roll_threshold 10 +init_velocity_threshold 0.015 +init_timeout 1000 +init_timeout_enable 6 +init_display_status 1 +recovery_timeout 800 + +display_video 1 +display_model_texture 0 +display_tri_mask 0 +display_model_wireframe 0 +display_results 1 +display_track_points 0 + +detect_strip_area_threshold 1000 +detect_strip_angle_threshold 0.15 +detect_strip_ratio_threshold 0.2 +detect_strip_perfect_ratio 6.848 +detect_strip_roi_y_offset 0 +detect_strip_roi_width 2 +detect_strip_roi_height 4 + +smoothing_factors + 5 25 -2 100 -1 50 25 0 +#translation rotation action_units eyebrows mouth gaze eye_closure other + +process_eyes 1 +leye_closing_au 12 +reye_closing_au 12 +eye_h_rotation_au 15 +eye_v_rotation_au 16 +eye_points_coords +162 1.0 0.0 0.0 +157 0.0 0.0 1.0 + +bdts_points_use + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +#3.5 3.6 3.7 3.8 3.11 3.12 8.1 8.2 8.3 8.4 9.1 9.2 9.3 4.1 4.2 4.3 4.4 4.5 4.6 2.1 + +bdts_points_angle + 0 0 20 -20 20 -20 0 0 20 -20 20 -20 0 25 -25 20 -20 20 -20 0 0 + +model_filename candide3-exp.wfm +fdp_filename candide3.fdp +bdts_data_path bdtsdata + +au_use + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +#AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 + +au_names +au_nose_wrinkler +au_jaw_z_push +au_jaw_x_push +au_jaw_drop +au_lower_lip_drop +au_upper_lip_raiser +au_lip_stretcher_left +au_lip_corner_depressor +au_lip_presser +au_left_outer_brow_raiser +au_left_inner_brow_raiser +au_left_brow_lowerer +au_leye_closed +au_lid_tightener +au_upper_lid_raiser +au_rotate_eyes_left +au_rotate_eyes_down +au_lower_lip_x_push +au_lip_stretcher_right +au_right_outer_brow_raiser +au_right_inner_brow_raiser +au_right_brow_lowerer +au_reye_closed + + +ekf_sensitivity + 100 100 100 100 100 100 100 100 100 100 100 200 200 100 100 400 400 100 100 100 100 300 300 100 100 400 400 100 100 +#x y z rx ry rz AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 + +au_gravity + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +#AU1 AU2 AU3 AU4 AU5 AU6 AU7 AU8 AU9 AU10 AU11 AU12 AU13 AU14 AU15 AU16 AU17 AU18 AU19 AU20 AU21 AU22 AU23 + +gravity_threshold 0.0 + +camera_focus 3 + +tex_size 512 + +ROI_num 14 + +#ROI 0 - nose region (setting default values) +feature_points_num[0] 7 +feature_points_tri_use_num[0] 21 +feature_points_tri_use_list[0] +48 154 54 55 152 45 46 47 103 109 +108 107 106 101 104 110 111 105 99 102 +100 +bound_rect_modifiers[0] -10 -10 -20 10 +feature_points_min_distance[0] 0.78125 +feature_point_block_size[0] 0.46875 +search_range[0] 4.0625 4.0625 +refine_range[0] 0.3125 0.3125 +patch_size[0] 4.6875 +match_method[0] 5 +bad_match_threshold[0] 0.35 +sensitivity_falloff[0] 10 +init_angle[0] 0 +feature_points_coords[0] +97 0.18 0.54 0.28 +90 0.18 0.28 0.54 +51 0.76 0.09 0.15 +97 0.36 0.10 0.54 +90 0.36 0.54 0.10 +69 0.55 0.10 0.35 +67 0.55 0.35 0.10 + +#ROI 1 - left eyebrow region +feature_points_num[1] 4 +feature_points_tri_use_num[1] 2 +feature_points_tri_use_list[1] +25 26 +bound_rect_modifiers[1] 2 2 40 2 +feature_points_coords[1] +25 0.0 0.0 1.0 +26 0.15 0.1 0.75 +25 1.0 0.0 0.0 +26 0.2 0.4 0.4 + +#ROI 2 - right eyebrow region +feature_points_num[2] 4 +feature_points_tri_use_num[2] 2 +feature_points_tri_use_list[2] +33 34 +bound_rect_modifiers[2] 2 2 40 2 +feature_points_coords[2] +33 0.15 0.75 0.1 +34 1.0 0.0 0.0 +33 0.0 0.0 1.0 +33 0.2 0.4 0.4 + +#ROI 3 - left outer eye corner region +feature_points_num[3] 3 +feature_points_tri_use_num[3] 4 +feature_points_tri_use_list[3] +112 23 39 115 +bound_rect_modifiers[3] -5 -5 -25 -25 +feature_points_coords[3] +23 0 0.2 0.8 +119 0.8351877 0.08414026 0.08067206 +116 0.3218788 0.4626164 0.2155048 + +#ROI 4 - right outer eye corner region +feature_points_num[4] 3 +feature_points_tri_use_num[4] 4 +feature_points_tri_use_list[4] +142 36 42 145 +bound_rect_modifiers[4] -5 -5 -25 -25 +feature_points_coords[4] +36 0 0.8 0.2 +151 0.8566859 0.0405132 0.1028009 +146 0.2871178 0.3467231 0.366159 + +#ROI 5 - left inner eye corner region +feature_points_num[5] 3 +feature_points_tri_use_num[5] 5 +feature_points_tri_use_list[5] +122 28 99 100 125 +bound_rect_modifiers[5] -5 -5 -25 -40 +feature_points_coords[5] +99 0.2 0.1 0.7 +122 0.2664618 0.3707059 0.3628323 +125 0.387767 0.4655813 0.1466517 + +#ROI 6 - right inner eye corner region +feature_points_num[6] 3 +feature_points_tri_use_num[6] 5 +feature_points_tri_use_list[6] +132 31 102 104 135 +bound_rect_modifiers[6] -5 -5 -25 -40 +feature_points_coords[6] +102 0.2 0.7 0.1 +132 0.3673472 0.2426805 0.3899724 +135 0.2833096 0.1345753 0.5821151 + +#ROI 7 - lower lip region +feature_points_num[7] 6 +feature_points_tri_use_num[7] 6 +feature_points_tri_use_list[7] +79 80 82 84 52 53 +bound_rect_modifiers[7] 0 0 0 -80 +refine_range[7] 0.5 1 +patch_size[7] 7 +feature_points_coords[7] +80 0.0 0.0 1.0 +80 1.0 0.0 0.0 +80 0.0 1.0 0.0 +79 0.0 0.0 1.0 +82 0.1 0.3 0.6 +84 0.1 0.6 0.3 + +#ROI 8 - upper lip region +feature_points_num[8] 6 +feature_points_tri_use_num[8] 10 +feature_points_tri_use_list[8] +69 49 155 51 50 153 44 67 57 60 +bound_rect_modifiers[8] 0 0 -70 0 +refine_range[8] 0.5 1 +patch_size[8] 7 +feature_points_coords[8] +67 0.1 0.45 0.45 +69 0.1 0.45 0.45 +57 0.0 0.0 1.0 +60 0.0 1.0 0.0 +51 0.0 1.0 0.0 +51 0.3 0.7 0.0 + +#ROI 9 - left lip corner region +feature_points_num[9] 4 +feature_points_tri_use_num[9] 4 +feature_points_tri_use_list[9] +87 89 81 68 +bound_rect_modifiers[9] 0 0 -35 -35 +refine_range[9] 1 1 +patch_size[9] 7 +feature_points_coords[9] +87 0.0 0.0 1.0 +87 0.2 0.0 0.8 +66 0.33 0.33 0.34 +72 0.33 0.33 0.34 + +#ROI 10 - right lip corner region +feature_points_num[10] 4 +feature_points_tri_use_num[10] 4 +feature_points_tri_use_list[10] +94 96 83 70 +bound_rect_modifiers[10] 0 0 -35 -35 +refine_range[10] 1 1 +patch_size[10] 7 +feature_points_coords[10] +94 0.0 1.0 0.0 +94 0.2 0.8 0.0 +64 0.33 0.33 0.34 +78 0.33 0.33 0.34 + +#ROI 11 - jaw region +feature_points_num[11] 4 +feature_points_tri_use_num[11] 2 +feature_points_tri_use_list[11] +52 53 +bound_rect_modifiers[11] 0 0 -30 0 +refine_range[11] 0.5 1 +patch_size[11] 7 +bad_match_threshold[11] 0.6 +feature_points_coords[11] +52 0.4 0.3 0.3 +53 0.4 0.3 0.3 +87 0.0 0.5 0.5 +94 0.0 0.5 0.5 + +feature_points_num[12] 6 +feature_points_tri_use_num[12] 3 +feature_points_tri_use_list[12] +188 189 190 191 +bound_rect_modifiers[12] -30 0 0 -30 +init_angle[12] -20 + +feature_points_num[13] 6 +feature_points_tri_use_num[13] 3 +feature_points_tri_use_list[13] +192 193 194 195 +bound_rect_modifiers[13] 0 -30 0 -30 +init_angle[13] 20 + +recovery_frames 4 +global_bad_match_threshold 0.5 +visibility_check 1 + +rotation_limit +-1.570796 1.570796 + 1.570796 4.712389 +-3.2 3.2 + +translation_limit +-4 4 +-3 3 + 0 11 + +action_unit_limit +-0.5 0.5 +-0.5 0.5 +-1.0 1.0 +-0.05 1.2 +-0.05 1.5 +-0.05 1.5 +-1.0 1.0 +-1.5 1.5 +-1.5 1.5 +-1.5 2 +-1.5 2 +-1 1.5 +-2.8 2.8 +-1.5 1.5 +-1.5 1.5 +-100 100 +-100 100 +-1.5 1.5 +-1.5 1.5 +-1.5 1.5 +-1.5 1.5 +-1.5 1.5 +-2.8 2.8 + + From 8e1c3c32d11adcee5a7b3f28e7107f322147ec3b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 20 Oct 2015 16:24:17 -0700 Subject: [PATCH 0276/1003] additions to debug and also to handle some two handed situations --- examples/controllers/handControllerGrab.js | 93 ++++++++++++++++++---- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d2ad2aa4be..f912761b43 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1,8 +1,9 @@ -// hydraGrab.js +// handControllerGrab.js // examples // // Created by Eric Levin on 9/2/15 // Additions by James B. Pollack @imgntn on 9/24/2015 +// Additions By Seth Alves on 10/20/2015 // Copyright 2015 High Fidelity, Inc. // // Grabs physically moveable entities with hydra-like controllers; it works for either near or far objects. @@ -13,6 +14,13 @@ Script.include("../libraries/utils.js"); + +//////////////////////////////////////////////////////////// +// +// add lines where the hand ray picking is happening +// +var DEBUG_HAND_RAY_PICKING = false; + ///////////////////////////////////////////////////////////////// // // these tune time-averaging and "on" value for analog trigger @@ -43,9 +51,9 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.2; // max length of pick-ray for close grabbing to be selected +var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things -var PICK_BACKOFF_DISTANCE = 0.1; // helps when hand is intersecting the grabble object +var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object ///////////////////////////////////////////////////////////////// // @@ -91,6 +99,8 @@ var DEFAULT_GRABBABLE_DATA = { invertSolidWhileHeld: false }; +var disabledHand ='none'; + function getTag() { return "grab-" + MyAvatar.sessionUUID; } @@ -188,6 +198,19 @@ function MyController(hand, triggerAction) { } + this.debugLine = function(closePoint, farPoint, color){ + Entities.addEntity({ + type: "Line", + name: "Debug Line", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: 0.1 + }); + } + this.lineOn = function(closePoint, farPoint, color) { // draw a line if (this.pointer === null) { @@ -249,11 +272,17 @@ function MyController(hand, triggerAction) { } this.search = function() { + //if this hand is the one that's disabled, we don't want to search for anything at all + if (this.hand === disabledHand) { + return; + } + if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); return; } + // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); var distantPickRay = { @@ -267,6 +296,13 @@ function MyController(hand, triggerAction) { length: NEAR_PICK_MAX_DISTANCE }; + var otherPickRay = { + origin: handPosition, + direction: Quat.getRight(this.getHandRotation()), + length: NEAR_PICK_MAX_DISTANCE + }; + + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); // don't pick 60x per second. do this check after updating the line so it's not jumpy. @@ -276,7 +312,7 @@ function MyController(hand, triggerAction) { } this.lastPickTime = now; - var pickRays = [distantPickRay, palmPickRay]; + var pickRays = [distantPickRay, palmPickRay, otherPickRay]; for (var index=0; index < pickRays.length; ++index) { var pickRay = pickRays[index]; var directionNormalized = Vec3.normalize(pickRay.direction); @@ -286,15 +322,34 @@ function MyController(hand, triggerAction) { direction: pickRay.direction }; + if (DEBUG_HAND_RAY_PICKING) + this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { + red: 0, + green: 255, + blue: 0 + }) + var intersection = Entities.findRayIntersection(pickRayBacked, true); + if (intersection.intersects && intersection.properties.locked === 0) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, - intersection.entityID, - DEFAULT_GRABBABLE_DATA); + + //this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); + if (grabbableData["turnOffOppositeBeam"] === true) { + if (this.hand === RIGHT_HAND) { + disabledHand = LEFT_HAND; + } else { + disabledHand = RIGHT_HAND; + } + + } else { + disabledHand = 'none'; + } + if (grabbableData.grabbable === false) { this.grabbedEntity = null; continue; @@ -386,9 +441,7 @@ function MyController(hand, triggerAction) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. - var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * - DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); - + var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); // how far did avatar move this timestep? var currentPosition = MyAvatar.position; var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); @@ -438,9 +491,7 @@ function MyController(hand, triggerAction) { this.currentObjectTime = now; // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, - DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), - Quat.inverse(this.handPreviousRotation)); + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), Quat.inverse(this.handPreviousRotation)); this.handPreviousRotation = handRotation; this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); @@ -459,15 +510,23 @@ function MyController(hand, triggerAction) { this.nearGrabbing = function() { var now = Date.now(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + var turnOffOtherHand = grabbableData["turnOffOtherHand"]; + if (turnOffOtherHand === true) { + //don't activate the second hand grab because the script is handling the second hand logic + return; + } + if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); return; } + this.lineOff(); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, - ["position", "rotation", "gravity", "ignoreForCollisions"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity", "ignoreForCollisions"]); this.activateEntity(this.grabbedEntity, grabbedProperties); var handRotation = this.getHandRotation(); @@ -705,6 +764,10 @@ function MyController(hand, triggerAction) { this.release = function() { + if(this.hand!==disabledHand){ + //release the disabled hand when we let go with the main one + disabledHand='none'; + } this.lineOff(); if (this.grabbedEntity !== null) { From eacd6af03232c15e5fc5220d046700eb52427fa3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 20 Oct 2015 14:14:13 -0700 Subject: [PATCH 0277/1003] 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 5cd2786c1d4e71a28e47dac550ee2a24b7835d57 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 20 Oct 2015 16:37:05 -0700 Subject: [PATCH 0278/1003] First pass at Rig timeScaling and blending between slow, walk and run. --- .../defaultAvatar_full/avatar-animation.json | 17 ++++++- libraries/animation/src/Rig.cpp | 44 +++++++++++++++++-- libraries/animation/src/Rig.h | 4 ++ libraries/shared/src/GLMHelpers.h | 5 +++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index ea4ef63d16..e75e806df6 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -4,7 +4,7 @@ "id": "ikOverlay", "type": "overlay", "data": { - "alpha": 1.0, + "alpha": 0.0, "boneSet": "fullBody" }, "children": [ @@ -532,7 +532,8 @@ "alpha": 0.0, "sync": true, "timeScale": 1.0, - "timeScaleVar": "walkTimeScale" + "timeScaleVar": "walkTimeScale", + "alphaVar": "walkAlpha" }, "children": [ { @@ -558,6 +559,18 @@ "loopFlag": true }, "children": [] + }, + { + "id": "walkFwdRun", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx", + "startFrame": 0.0, + "endFrame": 21.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] } ] }, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 25f28a3f64..82cc19f18c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -408,6 +408,41 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return _jointStates[jointIndex].getTransform(); } +void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut) { + + // filter speed using a moving average. + _averageForwardSpeed.updateAverage(speed); + speed = _averageForwardSpeed.getAverage(); + + const int NUM_FWD_SPEEDS = 3; + float FWD_SPEEDS[NUM_FWD_SPEEDS] = { 0.3f, 1.4f, 2.7f }; // m/s + + // first calculate alpha by lerping between speeds. + float alpha = 0.0f; + if (speed <= FWD_SPEEDS[0]) { + alpha = 0.0f; + } else if (speed > FWD_SPEEDS[NUM_FWD_SPEEDS - 1]) { + alpha = (float)(NUM_FWD_SPEEDS - 1); + } else { + for (int i = 0; i < NUM_FWD_SPEEDS - 1; i++) { + if (FWD_SPEEDS[i] < speed && speed < FWD_SPEEDS[i + 1]) { + alpha = (float)i + ((speed - FWD_SPEEDS[i]) / (FWD_SPEEDS[i + 1] - FWD_SPEEDS[i])); + break; + } + } + } + + // now keeping the alpha fixed compute the timeScale. + // NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear. + int prevIndex = glm::floor(alpha); + int nextIndex = glm::ceil(alpha); + float animSpeed = lerp(FWD_SPEEDS[prevIndex], FWD_SPEEDS[nextIndex], (float)glm::fract(alpha)); + float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed); + + *alphaOut = alpha; + *timeScaleOut = timeScale; +} + void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -435,10 +470,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // sine wave LFO var for testing. static float t = 0.0f; - _animVars.set("sine", static_cast(0.5 * sin(t) + 0.5)); + _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); - const float ANIM_WALK_SPEED = 1.4f; // m/s - _animVars.set("walkTimeScale", glm::clamp(0.5f, 2.0f, glm::length(localVel) / ANIM_WALK_SPEED)); + float walkAlpha, walkTimeScale; + calcWalkForwardAlphaAndTimeScale(glm::length(localVel), &walkAlpha, &walkTimeScale); + + _animVars.set("walkTimeScale", walkTimeScale); + _animVars.set("walkAlpha", walkAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 71c27e7213..c6f21deba6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -42,6 +42,7 @@ #include "AnimNode.h" #include "AnimNodeLoader.h" +#include "SimpleMovingAverage.h" class AnimationHandle; typedef std::shared_ptr AnimationHandlePointer; @@ -206,6 +207,7 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); + void calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut); QVector _jointStates; int _rootJointIndex = -1; @@ -240,6 +242,8 @@ public: float _desiredStateAge = 0.0f; float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; + + SimpleMovingAverage _averageForwardSpeed{ 25 }; }; #endif /* defined(__hifi__Rig__) */ diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 7bdd3bf2de..9c1bbe23a4 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -183,6 +183,11 @@ T toNormalizedDeviceScale(const T& value, const T& size) { #define PITCH(euler) euler.x #define ROLL(euler) euler.z +// float - linear interpolate +inline float lerp(float x, float y, float a) { + return x * (1.0f - a) + (y * a); +} + // vec2 lerp - linear interpolate template glm::detail::tvec2 lerp(const glm::detail::tvec2& x, const glm::detail::tvec2& y, T a) { From 3d2f00c609fc5ee48afcbaec5ffe56b4e0acc11d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 20 Oct 2015 17:01:45 -0700 Subject: [PATCH 0279/1003] 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 0280/1003] 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 f16b62df83767642ee11f522b00ed1bdccb0317b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 20 Oct 2015 17:34:35 -0700 Subject: [PATCH 0281/1003] - grab script: possible fix for line disappearing - grab script: go from 3 pickrays back to 1 - grab script: put "sphere" test back in, but with fixed distance test --- examples/controllers/handControllerGrab.js | 102 ++++++++++++--------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index f912761b43..9f174bd3e6 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -19,7 +19,7 @@ Script.include("../libraries/utils.js"); // // add lines where the hand ray picking is happening // -var DEBUG_HAND_RAY_PICKING = false; +var WANT_DEBUG = false; ///////////////////////////////////////////////////////////////// // @@ -193,22 +193,23 @@ function MyController(hand, triggerAction) { }; this.setState = function(newState) { - // print("STATE: " + this.state + " --> " + newState); + if (WANT_DEBUG) { + print("STATE: " + this.state + " --> " + newState); + } this.state = newState; } - this.debugLine = function(closePoint, farPoint, color){ - Entities.addEntity({ - type: "Line", - name: "Debug Line", - dimensions: LINE_ENTITY_DIMENSIONS, - visible: true, - position: closePoint, - linePoints: [ZERO_VEC, farPoint], - color: color, - lifetime: 0.1 - }); + Entities.addEntity({ + type: "Line", + name: "Debug Line", + dimensions: LINE_ENTITY_DIMENSIONS, + visible: true, + position: closePoint, + linePoints: [ZERO_VEC, farPoint], + color: color, + lifetime: 0.1 + }); } this.lineOn = function(closePoint, farPoint, color) { @@ -226,14 +227,13 @@ function MyController(hand, triggerAction) { }); } else { var age = Entities.getEntityProperties(this.pointer, "age").age; - Entities.editEntity(this.pointer, { + this.pointer = Entities.editEntity(this.pointer, { position: closePoint, linePoints: [ZERO_VEC, farPoint], color: color, lifetime: age + LIFETIME }); } - }; this.lineOff = function() { @@ -282,7 +282,6 @@ function MyController(hand, triggerAction) { return; } - // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); var distantPickRay = { @@ -290,29 +289,17 @@ function MyController(hand, triggerAction) { direction: Quat.getUp(this.getHandRotation()), length: PICK_MAX_DISTANCE }; - var palmPickRay = { - origin: handPosition, - direction: Quat.getFront(this.getHandRotation()), - length: NEAR_PICK_MAX_DISTANCE - }; - - var otherPickRay = { - origin: handPosition, - direction: Quat.getRight(this.getHandRotation()), - length: NEAR_PICK_MAX_DISTANCE - }; - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - // don't pick 60x per second. do this check after updating the line so it's not jumpy. + // don't pick 60x per second. + var pickRays = []; var now = Date.now(); - if (now - this.lastPickTime < MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { - return; + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + pickRays = [distantPickRay]; + this.lastPickTime = now; } - this.lastPickTime = now; - var pickRays = [distantPickRay, palmPickRay, otherPickRay]; for (var index=0; index < pickRays.length; ++index) { var pickRay = pickRays[index]; var directionNormalized = Vec3.normalize(pickRay.direction); @@ -322,12 +309,13 @@ function MyController(hand, triggerAction) { direction: pickRay.direction }; - if (DEBUG_HAND_RAY_PICKING) + if (WANT_DEBUG) { this.debugLine(pickRayBacked.origin, Vec3.multiply(pickRayBacked.direction, NEAR_PICK_MAX_DISTANCE), { red: 0, green: 255, blue: 0 }) + } var intersection = Entities.findRayIntersection(pickRayBacked, true); @@ -336,7 +324,6 @@ function MyController(hand, triggerAction) { var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; - //this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); if (grabbableData["turnOffOppositeBeam"] === true) { @@ -345,7 +332,6 @@ function MyController(hand, triggerAction) { } else { disabledHand = RIGHT_HAND; } - } else { disabledHand = 'none'; } @@ -380,6 +366,35 @@ function MyController(hand, triggerAction) { } } } + + if (this.grabbedEntity === null) { + // forward ray test failed, try sphere test. + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = PICK_MAX_DISTANCE; + var i, props, distance; + for (i = 0; i < nearbyEntities.length; i++) { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (grabbableData.grabbable === false) { + continue; + } + var propsForCandidate = + Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]); + distance = Vec3.distance(propsForCandidate.position, handPosition); + if (distance < minDistance && props.name !== "pointer") { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + props = propsForCandidate; + } + } + if (this.grabbedEntity === null) { + return; + } else if (props.locked === 0 && props.collisionsWillMove === 1) { + this.setState(STATE_NEAR_GRABBING); + } else if (props.collisionsWillMove === 0) { + // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event + this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } + } }; this.distanceHolding = function() { @@ -441,7 +456,8 @@ function MyController(hand, triggerAction) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. - var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); + var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * + DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); // how far did avatar move this timestep? var currentPosition = MyAvatar.position; var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition); @@ -491,7 +507,10 @@ function MyController(hand, triggerAction) { this.currentObjectTime = now; // this doubles hand rotation - var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, handRotation, DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), Quat.inverse(this.handPreviousRotation)); + var handChange = Quat.multiply(Quat.slerp(this.handPreviousRotation, + handRotation, + DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR), + Quat.inverse(this.handPreviousRotation)); this.handPreviousRotation = handRotation; this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation); @@ -526,7 +545,8 @@ function MyController(hand, triggerAction) { this.lineOff(); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity", "ignoreForCollisions"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, + ["position", "rotation", "gravity", "ignoreForCollisions"]); this.activateEntity(this.grabbedEntity, grabbedProperties); var handRotation = this.getHandRotation(); @@ -764,9 +784,9 @@ function MyController(hand, triggerAction) { this.release = function() { - if(this.hand!==disabledHand){ + if(this.hand !== disabledHand){ //release the disabled hand when we let go with the main one - disabledHand='none'; + disabledHand = 'none'; } this.lineOff(); From ed480f651f06a916b40988a7494277c7b23e66be Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 20 Oct 2015 17:43:31 -0700 Subject: [PATCH 0282/1003] missed a line --- examples/controllers/handControllerGrab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9f174bd3e6..1ed3406c61 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -49,6 +49,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // near grabbing // +var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected From 7dcbb5849ae0ccfe45db2b717aa88829f78840cc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 20 Oct 2015 17:44:12 -0700 Subject: [PATCH 0283/1003] another bug --- 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 1ed3406c61..9176394a95 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -381,7 +381,7 @@ function MyController(hand, triggerAction) { var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]); distance = Vec3.distance(propsForCandidate.position, handPosition); - if (distance < minDistance && props.name !== "pointer") { + if (distance < minDistance && propsForCandidate.name !== "pointer") { this.grabbedEntity = nearbyEntities[i]; minDistance = distance; props = propsForCandidate; From 6f7719e9e9cb021545f807129ad5e8c8a4eb2b8a Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 20 Oct 2015 17:44:24 -0700 Subject: [PATCH 0284/1003] 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 0285/1003] 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 311254c395d5db9dd11bb2999bac126b23feab79 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 20 Oct 2015 18:26:12 -0700 Subject: [PATCH 0286/1003] require user-data to have wantsTrigger in it before non-colliding calls will happen --- examples/controllers/handControllerGrab.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9176394a95..a087236abd 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -372,10 +372,11 @@ function MyController(hand, triggerAction) { // forward ray test failed, try sphere test. var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); var minDistance = PICK_MAX_DISTANCE; - var i, props, distance; + var i, props, distance, grabbableData; for (i = 0; i < nearbyEntities.length; i++) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { + var grabbableDataForCandidate = + getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (grabbableDataForCandidate.grabbable === false) { continue; } var propsForCandidate = @@ -385,13 +386,14 @@ function MyController(hand, triggerAction) { this.grabbedEntity = nearbyEntities[i]; minDistance = distance; props = propsForCandidate; + grabbableData = grabbableDataForCandidate; } } if (this.grabbedEntity === null) { return; } else if (props.locked === 0 && props.collisionsWillMove === 1) { this.setState(STATE_NEAR_GRABBING); - } else if (props.collisionsWillMove === 0) { + } else if (props.collisionsWillMove === 0 & grabbableData.wantsTrigger) { // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); } From f85ef9267baeebdf1dc8ca4e7b49a22ff9564948 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 20 Oct 2015 18:29:27 -0700 Subject: [PATCH 0287/1003] oops --- 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 a087236abd..80fb4c8e40 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -393,7 +393,7 @@ function MyController(hand, triggerAction) { return; } else if (props.locked === 0 && props.collisionsWillMove === 1) { this.setState(STATE_NEAR_GRABBING); - } else if (props.collisionsWillMove === 0 & grabbableData.wantsTrigger) { + } else if (props.collisionsWillMove === 0 && grabbableData.wantsTrigger) { // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); } From d34ad2af42854e38415d9b1debea048c3a741c05 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 20 Oct 2015 21:01:20 -0700 Subject: [PATCH 0288/1003] improved findEntities(sphere) to check against Non-AABBox of entities --- libraries/entities/src/EntityTreeElement.cpp | 84 +++++++++++++++++--- libraries/entities/src/EntityTreeElement.h | 7 +- 2 files changed, 77 insertions(+), 14 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 56ab27836c..34433cdbb1 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -609,25 +609,83 @@ EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector& foundEntities) const { float compareRadius = searchRadius * searchRadius; forEachEntity([&](EntityItemPointer entity) { - // For iteration like this, avoid the use of square roots by comparing distances squared - float distanceSquared = glm::length2(entity->getPosition() - searchPosition); - float otherRadius = entity->getRadius(); - if (distanceSquared < (compareRadius + (otherRadius * otherRadius))) { + + AABox entityBox = entity->getAABox(); + + // if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case + glm::vec3 penetration; + if (entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { + + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better + // FIXME - consider allowing the entity to determine penetration so that + // entities could presumably dull actuall hull testing if they wanted to + + // determine the worldToEntityMatrix that doesn't include scale because + // we're going to use the registration aware aa box in the entity frame + glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); + glm::mat4 translation = glm::translate(entity->getPosition()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + + glm::vec3 dimensions = entity->getDimensions(); + glm::vec3 registrationPoint = entity->getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint); + + AABox entityFrameBox(corner, dimensions); + + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(searchPosition, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, searchRadius, penetration)) { + foundEntities.push_back(entity); + } + } + }); +} + +void EntityTreeElement::getEntities(const AACube& cube, QVector& foundEntities) { + forEachEntity([&](EntityItemPointer entity) { + AABox entityBox = entity->getAABox(); + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better + // FIXME - consider allowing the entity to determine penetration so that + // entities could presumably dull actuall hull testing if they wanted to + // FIXME - is there an easy way to translate the search cube into something in the + // entity frame that can be easily tested against? + // simple algorithm is probably: + // if target box is fully inside search box == yes + // if search box is fully inside target box == yes + // for each face of search box: + // translate the triangles of the face into the box frame + // test the triangles of the face against the box? + // if translated search face triangle intersect target box + // add to result + // + + // If the entities AABox touches the search cube then consider it to be found + if (entityBox.touches(cube)) { foundEntities.push_back(entity); } }); } -// TODO: change this to use better bounding shape for entity than sphere -void EntityTreeElement::getEntities(const AACube& box, QVector& foundEntities) { - AACube entityCube; +void EntityTreeElement::getEntities(const AABox& box, QVector& foundEntities) { forEachEntity([&](EntityItemPointer entity) { - float radius = entity->getRadius(); - // NOTE: we actually do cube-cube collision queries here, which is sloppy but good enough for now - // TODO: decide whether to replace entityCube-cube query with sphere-cube (requires a square root - // but will be slightly more accurate). - entityCube.setBox(entity->getPosition() - glm::vec3(radius), 2.0f * radius); - if (entityCube.touches(box)) { + AABox entityBox = entity->getAABox(); + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better + // FIXME - consider allowing the entity to determine penetration so that + // entities could presumably dull actuall hull testing if they wanted to + // FIXME - is there an easy way to translate the search cube into something in the + // entity frame that can be easily tested against? + // simple algorithm is probably: + // if target box is fully inside search box == yes + // if search box is fully inside target box == yes + // for each face of search box: + // translate the triangles of the face into the box frame + // test the triangles of the face against the box? + // if translated search face triangle intersect target box + // add to result + // + + // If the entities AABox touches the search cube then consider it to be found + if (entityBox.touches(box)) { foundEntities.push_back(entity); } }); diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 0a47542e65..cb5e35945e 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -180,7 +180,12 @@ public: /// finds all entities that touch a box /// \param box the query box /// \param entities[out] vector of non-const EntityItemPointer - void getEntities(const AACube& box, QVector& foundEntities); + void getEntities(const AACube& cube, QVector& foundEntities); + + /// finds all entities that touch a box + /// \param box the query box + /// \param entities[out] vector of non-const EntityItemPointer + void getEntities(const AABox& box, QVector& foundEntities); EntityItemPointer getEntityWithID(uint32_t id) const; EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const; From 053aca2e3695ebb8aa9ffbeae1c1886f5e3a7813 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 20 Oct 2015 21:12:46 -0700 Subject: [PATCH 0289/1003] 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 0290/1003] 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 0291/1003] 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 1c5dc0374abd075342706f941e0c4450ca1a65d4 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 21 Oct 2015 10:16:31 -0700 Subject: [PATCH 0292/1003] Added wantsTrigger to user data for a couple items to avoid breakage with new grab script changes --- unpublishedScripts/hiddenEntityReset.js | 14 +++++++++----- unpublishedScripts/masterReset.js | 7 ++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index fde97beeb5..19a7a5ee5e 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -54,7 +54,6 @@ MasterReset = function() { var resetKey = "resetMe"; - var GRABBABLE_DATA_KEY = "grabbableKey"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -97,8 +96,6 @@ z: 505.78 }); - - createCombinedArmChair({ x: 549.29, y: 494.9, @@ -487,7 +484,6 @@ grabbableKey: { invertSolidWhileHeld: true } - }) }); @@ -533,6 +529,9 @@ resetMe: true, on: true, type: "Hall Light" + }, + grabbableKey: { + wantsTrigger: true } }) }); @@ -628,6 +627,9 @@ resetMe: true, on: true, type: "Garage Light" + }, + grabbableKey: { + wantsTrigger: true } }) }); @@ -759,6 +761,7 @@ grabbableKey: { invertSolidWhileHeld: true } + }) }; var dice1 = Entities.addEntity(diceProps); @@ -851,6 +854,7 @@ grabbableKey: { invertSolidWhileHeld: true } + }) }); } @@ -1176,7 +1180,7 @@ y: 0.05, z: 0.25 } - } ]; + }]; var modelURL, entity; for (i = 0; i < blockTypes.length; i++) { diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 7316da57d1..54f29b4259 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -27,7 +27,6 @@ var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTa MasterReset = function() { var resetKey = "resetMe"; - var GRABBABLE_DATA_KEY = "grabbableKey"; var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -505,6 +504,9 @@ MasterReset = function() { resetMe: true, on: true, type: "Hall Light" + }, + grabbableKey: { + wantsTrigger: true } }) }); @@ -600,6 +602,9 @@ MasterReset = function() { resetMe: true, on: true, type: "Garage Light" + }, + grabbableKey: { + wantsTrigger: true } }) }); From 246e46b69a8e6556aea29cdcaf4b678962f481a7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 10:46:50 -0700 Subject: [PATCH 0293/1003] implement improvement in sphere test --- .../src/EntityItemPropertiesDefaults.h | 4 +- libraries/entities/src/EntityTreeElement.cpp | 44 ++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 538d7dd890..06c6565d1b 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -21,8 +21,8 @@ // There is a minor performance gain when comparing/copying an existing glm::vec3 rather than // creating a new one on the stack so we declare the ZERO_VEC3 constant as an optimization. const glm::vec3 ENTITY_ITEM_ZERO_VEC3 = glm::vec3(0.0f); -const glm::vec3 ENTITY_ITEM_ONE_VEC3 = glm::vec3(1.0f, 1.0f, 1.0f); -const glm::vec3 ENTITY_ITEM_HALF_VEC3 = ENTITY_ITEM_ONE_VEC3 / 2.0f; +const glm::vec3 ENTITY_ITEM_ONE_VEC3 = glm::vec3(1.0f); +const glm::vec3 ENTITY_ITEM_HALF_VEC3 = glm::vec3(0.5f); const bool ENTITY_ITEM_DEFAULT_LOCKED = false; const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString(""); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 3ea6cfdbe8..57f49f2354 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -648,7 +648,6 @@ EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const // TODO: change this to use better bounding shape for entity than sphere void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector& foundEntities) const { - float compareRadius = searchRadius * searchRadius; forEachEntity([&](EntityItemPointer entity) { AABox entityBox = entity->getAABox(); @@ -657,26 +656,41 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc glm::vec3 penetration; if (entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) { - // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better + glm::vec3 dimensions = entity->getDimensions(); + // FIXME - consider allowing the entity to determine penetration so that // entities could presumably dull actuall hull testing if they wanted to + // FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better in particular + // can we handle the ellipsoid case better? We only currently handle perfect spheres + // with centered registration points + if (entity->getShapeType() == SHAPE_TYPE_SPHERE && + (dimensions.x == dimensions.y && dimensions.y == dimensions.z)) { - // determine the worldToEntityMatrix that doesn't include scale because - // we're going to use the registration aware aa box in the entity frame - glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); - glm::mat4 translation = glm::translate(entity->getPosition()); - glm::mat4 entityToWorldMatrix = translation * rotation; - glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + // NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the + // maximum bounding sphere, which is actually larger than our actual radius + float entityTrueRadius = dimensions.x / 2.0f; - glm::vec3 dimensions = entity->getDimensions(); - glm::vec3 registrationPoint = entity->getRegistrationPoint(); - glm::vec3 corner = -(dimensions * registrationPoint); + if (findSphereSpherePenetration(searchPosition, searchRadius, + entity->getCenterPosition(), entityTrueRadius, penetration)) { + foundEntities.push_back(entity); + } + } else { + // determine the worldToEntityMatrix that doesn't include scale because + // we're going to use the registration aware aa box in the entity frame + glm::mat4 rotation = glm::mat4_cast(entity->getRotation()); + glm::mat4 translation = glm::translate(entity->getPosition()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); - AABox entityFrameBox(corner, dimensions); + glm::vec3 registrationPoint = entity->getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint); - glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(searchPosition, 1.0f)); - if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, searchRadius, penetration)) { - foundEntities.push_back(entity); + AABox entityFrameBox(corner, dimensions); + + glm::vec3 entityFrameSearchPosition = glm::vec3(worldToEntityMatrix * glm::vec4(searchPosition, 1.0f)); + if (entityFrameBox.findSpherePenetration(entityFrameSearchPosition, searchRadius, penetration)) { + foundEntities.push_back(entity); + } } } }); From 68d43dc57661ae6aeeb4e8e1d03cfe07060a02c3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 21 Oct 2015 10:50:37 -0700 Subject: [PATCH 0294/1003] reduce number of toybox sound scripts --- examples/toys/AC_scripts/arcade_game_sound.js | 52 ------- examples/toys/AC_scripts/cat_purr_sound.js | 38 ----- .../toys/AC_scripts/dogs_barking_sound.js | 52 ------- examples/toys/AC_scripts/fireplace_sound.js | 38 ----- examples/toys/AC_scripts/insects_sound.js | 39 ----- examples/toys/AC_scripts/river_water_sound.js | 37 ----- examples/toys/AC_scripts/toybox_sounds.js | 143 ++++++++++++++++++ examples/toys/AC_scripts/windmill_sound.js | 38 ----- 8 files changed, 143 insertions(+), 294 deletions(-) delete mode 100644 examples/toys/AC_scripts/arcade_game_sound.js delete mode 100644 examples/toys/AC_scripts/cat_purr_sound.js delete mode 100644 examples/toys/AC_scripts/dogs_barking_sound.js delete mode 100644 examples/toys/AC_scripts/fireplace_sound.js delete mode 100644 examples/toys/AC_scripts/insects_sound.js delete mode 100644 examples/toys/AC_scripts/river_water_sound.js create mode 100644 examples/toys/AC_scripts/toybox_sounds.js delete mode 100644 examples/toys/AC_scripts/windmill_sound.js diff --git a/examples/toys/AC_scripts/arcade_game_sound.js b/examples/toys/AC_scripts/arcade_game_sound.js deleted file mode 100644 index 0f158aa574..0000000000 --- a/examples/toys/AC_scripts/arcade_game_sound.js +++ /dev/null @@ -1,52 +0,0 @@ -// Adds arcade game noises to Toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -var SOUND_URL = "http://hifi-public.s3.amazonaws.com/ryan/ARCADE_GAMES_VID.L.L.wav"; -var SOUND_POSITION = { x: 543.77, y: 495.07, z: 502.25 }; - -var MINUTE = 60 * 1000; -var PLAY_SOUND_INTERVAL = 1.5 * MINUTE; - -var audioOptions = { - position: SOUND_POSITION, - volume: .01, - loop: false, -}; - -var sound = SoundCache.getSound(SOUND_URL); -var injector = null; - -function playSound() { - print("Playing sound"); - if (injector) { - // try/catch in case the injector QObject has been deleted already - try { - injector.stop(); - } catch (e) { - } - } - injector = Audio.playSound(sound, audioOptions); -} - -function checkDownloaded() { - if (sound.downloaded) { - print("Sound downloaded."); - Script.clearInterval(checkDownloadedTimer); - Script.setInterval(playSound, PLAY_SOUND_INTERVAL); - playSound(); - } -} - -// Check once a second to see if the audio file has been downloaded -var checkDownloadedTimer = Script.setInterval(checkDownloaded, 1000); - -Script.scriptEnding.connect(function() { - if (injector) { - injector.stop(); - } -}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/cat_purr_sound.js b/examples/toys/AC_scripts/cat_purr_sound.js deleted file mode 100644 index 0ac05a1974..0000000000 --- a/examples/toys/AC_scripts/cat_purr_sound.js +++ /dev/null @@ -1,38 +0,0 @@ -// Adds a cat purring noise to Toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/Cat_Purring_Deep_Low_Snor.wav"; -var position = { x: 551.48, y: 495.60, z: 502.08}; -var audioOptions = { - position: position, - volume: .03, - loop: true -}; - -var sound = SoundCache.getSound(soundURL); -var injector = null; -var count = 300; - -Script.update.connect(function() { - if (count > 0) { - count--; - return; - } - - if (sound.downloaded && injector === null) { - print("Sound downloaded."); - injector = Audio.playSound(sound, audioOptions); - print("Playing: " + injector); - } -}); - -Script.scriptEnding.connect(function() { - if (injector !== null) { - injector.stop(); - } -}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/dogs_barking_sound.js b/examples/toys/AC_scripts/dogs_barking_sound.js deleted file mode 100644 index c362e1ac76..0000000000 --- a/examples/toys/AC_scripts/dogs_barking_sound.js +++ /dev/null @@ -1,52 +0,0 @@ -// Adds a dogs barking noise to Toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -var SOUND_URL = "http://hifi-public.s3.amazonaws.com/ryan/dogs_barking_1.L.wav"; -var SOUND_POSITION = { x: 523, y: 495, z: 469 }; - -var MINUTE = 60 * 1000; -var PLAY_SOUND_INTERVAL = 1 * MINUTE; - -var audioOptions = { - position: SOUND_POSITION, - volume: .05, - loop: false, -}; - -var sound = SoundCache.getSound(SOUND_URL); -var injector = null; - -function playSound() { - print("Playing sound"); - if (injector) { - // try/catch in case the injector QObject has been deleted already - try { - injector.stop(); - } catch (e) { - } - } - injector = Audio.playSound(sound, audioOptions); -} - -function checkDownloaded() { - if (sound.downloaded) { - print("Sound downloaded."); - Script.clearInterval(checkDownloadedTimer); - Script.setInterval(playSound, PLAY_SOUND_INTERVAL); - playSound(); - } -} - -// Check once a second to see if the audio file has been downloaded -var checkDownloadedTimer = Script.setInterval(checkDownloaded, 1000); - -Script.scriptEnding.connect(function() { - if (injector) { - injector.stop(); - } -}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/fireplace_sound.js b/examples/toys/AC_scripts/fireplace_sound.js deleted file mode 100644 index 76a7153c4b..0000000000 --- a/examples/toys/AC_scripts/fireplace_sound.js +++ /dev/null @@ -1,38 +0,0 @@ -// Adds a fireplace noise to Toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/demo/0619_Fireplace__Tree_B.L.wav"; -var position = { x: 551.61, y: 494.88, z: 502.00}; -var audioOptions = { - position: position, - volume: .08, - loop: true -}; - -var sound = SoundCache.getSound(soundURL); -var injector = null; -var count = 300; - -Script.update.connect(function() { - if (count > 0) { - count--; - return; - } - - if (sound.downloaded && injector === null) { - print("Sound downloaded."); - injector = Audio.playSound(sound, audioOptions); - print("Playing: " + injector); - } -}); - -Script.scriptEnding.connect(function() { - if (injector !== null) { - injector.stop(); - } -}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/insects_sound.js b/examples/toys/AC_scripts/insects_sound.js deleted file mode 100644 index 1783413901..0000000000 --- a/examples/toys/AC_scripts/insects_sound.js +++ /dev/null @@ -1,39 +0,0 @@ -// Adds an insect noise to Toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - - -var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/insects3.wav"; -var position = { x: 560, y: 495, z: 474}; -var audioOptions = { - position: position, - volume: .25, - loop: true -}; - -var sound = SoundCache.getSound(soundURL); -var injector = null; -var count = 300; - -Script.update.connect(function() { - if (count > 0) { - count--; - return; - } - - if (sound.downloaded && injector === null) { - print("Sound downloaded."); - injector = Audio.playSound(sound, audioOptions); - print("Playing: " + injector); - } -}); - -Script.scriptEnding.connect(function() { - if (injector !== null) { - injector.stop(); - } -}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/river_water_sound.js b/examples/toys/AC_scripts/river_water_sound.js deleted file mode 100644 index 30df67b515..0000000000 --- a/examples/toys/AC_scripts/river_water_sound.js +++ /dev/null @@ -1,37 +0,0 @@ -// Adds a river water sound to Toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/Water_Lap_River_Edge_Gentle.L.wav"; -var position = { x: 580, y: 493, z: 528}; -var audioOptions = { - position: position, - volume: .4, - loop: true -}; - -var sound = SoundCache.getSound(soundURL); -var injector = null; -var count = 300; - -Script.update.connect(function() { - if (count > 0) { - count--; - return; - } - - if (sound.downloaded && injector === null) { - print("Sound downloaded."); - injector = Audio.playSound(sound, audioOptions); - print("Playing: " + injector); - } -}); - -Script.scriptEnding.connect(function() { - if (injector !== null) { - injector.stop(); - } -}); \ No newline at end of file diff --git a/examples/toys/AC_scripts/toybox_sounds.js b/examples/toys/AC_scripts/toybox_sounds.js new file mode 100644 index 0000000000..87d2bcc75a --- /dev/null +++ b/examples/toys/AC_scripts/toybox_sounds.js @@ -0,0 +1,143 @@ +// +// toys/AC_scripts/toybox_sounds.js +// +// This script adds several sounds to the correct locations for toybox. +// By James B. Pollack @imgntn 10/21/2015 +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var soundMap = [{ + name: 'river water', + url: "http://hifi-public.s3.amazonaws.com/ryan/Water_Lap_River_Edge_Gentle.L.wav", + audioOptions: { + position: { + x: 580, + y: 493, + z: 528 + }, + volume: 0.4, + loop: true + } +}, { + name: 'windmill', + url: "http://hifi-public.s3.amazonaws.com/ryan/WINDMILL_Mono.wav", + audioOptions: { + position: { + x: 530, + y: 516, + z: 518 + }, + volume: 0.08, + loop: true + } +}, { + name: 'insects', + url: "http://hifi-public.s3.amazonaws.com/ryan/insects3.wav", + audioOptions: { + position: { + x: 560, + y: 495, + z: 474 + }, + volume: 0.25, + loop: true + } +}, { + name: 'fireplace', + url: "http://hifi-public.s3.amazonaws.com/ryan/demo/0619_Fireplace__Tree_B.L.wav", + audioOptions: { + position: { + x: 551.61, + y: 494.88, + z: 502.00 + }, + volume: 0.25, + loop: true + } +}, { + name: 'cat purring', + url: "http://hifi-public.s3.amazonaws.com/ryan/Cat_Purring_Deep_Low_Snor.wav", + audioOptions: { + position: { + x: 551.48, + y: 495.60, + z: 502.08 + }, + volume: 0.25, + loop: true + } +}, { + name: 'dogs barking', + url: "http://hifi-public.s3.amazonaws.com/ryan/dogs_barking_1.L.wav", + audioOptions: { + position: { + x: 551.61, + y: 494.88, + z: 502.00 + }, + volume: 0.15, + loop: false + }, + playAtInterval: 60 * 1000 +}]; + +function loadSounds() { + soundMap.forEach(function(soundData) { + soundData.sound = SoundCache.getSound(soundData.url); + }); +} + + +function playSound(soundData) { + if (soundData.injector) { + // try/catch in case the injector QObject has been deleted already + try { + soundData.injector.stop(); + } catch (e) {} + } + soundData.injector = Audio.playSound(soundData.sound, soundData.audioOptions); +} + +function checkDownloaded(soundData) { + if (soundData.sound.downloaded) { + Script.clearInterval(soundData.downloadTimer); + if (soundData.hasOwnProperty('playAtInterval')) { + soundData.playingInterval = Script.setInterval(function() { + playSound(soundData); + }, soundData.playAtInterval); + } else { + playSound(soundData); + } + + } +} + +function startCheckDownloadedTimers() { + soundMap.forEach(function(soundData) { + soundData.downloadTimer = Script.setInterval(function() { + checkDownloaded(soundData) + }, 1000); + }); +} + +Script.scriptEnding.connect(function() { + soundMap.forEach(function(soundData) { + if (soundData.hasOwnProperty("injector")) { + soundData.injector.stop(); + } + if (soundData.hasOwnProperty("downloadTimer")) { + Script.clearInterval(soundData.downloadTimer); + + } + if (soundData.hasOwnProperty("playingInterval")) { + Script.clearInterval(soundData.playingInterval); + } + }); + +}); + +loadSounds(); +startCheckDownloadedTimers(); \ No newline at end of file diff --git a/examples/toys/AC_scripts/windmill_sound.js b/examples/toys/AC_scripts/windmill_sound.js deleted file mode 100644 index 2072dbb9dd..0000000000 --- a/examples/toys/AC_scripts/windmill_sound.js +++ /dev/null @@ -1,38 +0,0 @@ -// Adds a windmill noise to toybox. -// By Ryan Karpf 10/20/2015 -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -var soundURL = "http://hifi-public.s3.amazonaws.com/ryan/WINDMILL_Mono.wav"; -var position = { x: 530, y: 516, z: 518}; -var audioOptions = { - position: position, - volume: .08, - loop: true -}; - -var sound = SoundCache.getSound(soundURL); -var injector = null; -var count = 300; - -Script.update.connect(function() { - if (count > 0) { - count--; - return; - } - - if (sound.downloaded && injector === null) { - print("Sound downloaded."); - injector = Audio.playSound(sound, audioOptions); - print("Playing: " + injector); - } -}); - -Script.scriptEnding.connect(function() { - if (injector !== null) { - injector.stop(); - } -}); \ No newline at end of file From 0b06cf1dfbf990b1e6c786906b6f9189e9a1e21b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 21 Oct 2015 10:53:32 -0700 Subject: [PATCH 0295/1003] cleanup --- examples/toys/AC_scripts/toybox_sounds.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/toys/AC_scripts/toybox_sounds.js b/examples/toys/AC_scripts/toybox_sounds.js index 87d2bcc75a..67985a5938 100644 --- a/examples/toys/AC_scripts/toybox_sounds.js +++ b/examples/toys/AC_scripts/toybox_sounds.js @@ -8,7 +8,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - var soundMap = [{ name: 'river water', url: "http://hifi-public.s3.amazonaws.com/ryan/Water_Lap_River_Edge_Gentle.L.wav", @@ -90,7 +89,6 @@ function loadSounds() { }); } - function playSound(soundData) { if (soundData.injector) { // try/catch in case the injector QObject has been deleted already @@ -103,10 +101,12 @@ function playSound(soundData) { function checkDownloaded(soundData) { if (soundData.sound.downloaded) { + Script.clearInterval(soundData.downloadTimer); + if (soundData.hasOwnProperty('playAtInterval')) { soundData.playingInterval = Script.setInterval(function() { - playSound(soundData); + playSound(soundData) }, soundData.playAtInterval); } else { playSound(soundData); @@ -118,23 +118,26 @@ function checkDownloaded(soundData) { function startCheckDownloadedTimers() { soundMap.forEach(function(soundData) { soundData.downloadTimer = Script.setInterval(function() { - checkDownloaded(soundData) + checkDownloaded(soundData); }, 1000); }); } Script.scriptEnding.connect(function() { soundMap.forEach(function(soundData) { + if (soundData.hasOwnProperty("injector")) { soundData.injector.stop(); } + if (soundData.hasOwnProperty("downloadTimer")) { Script.clearInterval(soundData.downloadTimer); - } + if (soundData.hasOwnProperty("playingInterval")) { Script.clearInterval(soundData.playingInterval); } + }); }); From ac2a60befb73dd11fe4d04a3cf90e8e8a6be3f03 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 10:57:41 -0700 Subject: [PATCH 0296/1003] 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 384a6a8154fb6841012e528d1079208a344859a0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 21 Oct 2015 11:36:49 -0700 Subject: [PATCH 0297/1003] rename /toys to /toybox, remove non toybox scripts, update master refs --- .../{toys => entityScripts}/breakdanceCore.js | 1 - .../{toys => entityScripts}/breakdanceToy.js | 0 examples/{toys => entityScripts}/grenade.js | 0 examples/{toys => entityScripts}/magBalls.js | 8 ++++---- .../magBalls/constants.js | 0 .../magBalls/debugUtils.js | 0 .../magBalls/edgeSpring.js | 0 .../{toys => entityScripts}/magBalls/graph.js | 0 .../magBalls/magBalls.js | 0 .../AC_scripts/flickeringLight.js | 0 .../AC_scripts/originalPositionResetter.js | 0 .../AC_scripts/toybox_sounds.js | 0 .../{toys => toybox}/basketball/createHoop.js | 0 .../{toys => toybox}/basketball/createRack.js | 0 .../basketball/createSingleBasketball.js | 0 .../blockers/createTestBlocks.js | 0 .../{toys => toybox}/bubblewand/createWand.js | 0 examples/{toys => toybox}/bubblewand/wand.js | 0 examples/{toys => toybox/cat}/cat.js | 0 examples/{toys => toybox}/doll/createDoll.js | 0 examples/{toys => toybox}/doll/doll.js | 0 .../flashlight/createFlashlight.js | 0 .../{toys => toybox}/flashlight/flashlight.js | 0 examples/{toys => toybox/lights}/lightSwitch.js | 2 +- .../ping_pong_gun/createPingPongGun.js | 0 .../ping_pong_gun/createTargets.js | 0 .../ping_pong_gun/pingPongGun.js | 0 .../ping_pong_gun/wallTarget.js | 0 .../spray_paint}/sprayPaintCan.js | 2 +- unpublishedScripts/hiddenEntityReset.js | 17 +++++++++-------- unpublishedScripts/masterReset.js | 16 ++++++++-------- 31 files changed, 23 insertions(+), 23 deletions(-) rename examples/{toys => entityScripts}/breakdanceCore.js (99%) rename examples/{toys => entityScripts}/breakdanceToy.js (100%) rename examples/{toys => entityScripts}/grenade.js (100%) rename examples/{toys => entityScripts}/magBalls.js (97%) rename examples/{toys => entityScripts}/magBalls/constants.js (100%) rename examples/{toys => entityScripts}/magBalls/debugUtils.js (100%) rename examples/{toys => entityScripts}/magBalls/edgeSpring.js (100%) rename examples/{toys => entityScripts}/magBalls/graph.js (100%) rename examples/{toys => entityScripts}/magBalls/magBalls.js (100%) rename examples/{toys => toybox}/AC_scripts/flickeringLight.js (100%) rename examples/{toys => toybox}/AC_scripts/originalPositionResetter.js (100%) rename examples/{toys => toybox}/AC_scripts/toybox_sounds.js (100%) rename examples/{toys => toybox}/basketball/createHoop.js (100%) rename examples/{toys => toybox}/basketball/createRack.js (100%) rename examples/{toys => toybox}/basketball/createSingleBasketball.js (100%) rename examples/{toys => toybox}/blockers/createTestBlocks.js (100%) rename examples/{toys => toybox}/bubblewand/createWand.js (100%) rename examples/{toys => toybox}/bubblewand/wand.js (100%) rename examples/{toys => toybox/cat}/cat.js (100%) rename examples/{toys => toybox}/doll/createDoll.js (100%) rename examples/{toys => toybox}/doll/doll.js (100%) rename examples/{toys => toybox}/flashlight/createFlashlight.js (100%) rename examples/{toys => toybox}/flashlight/flashlight.js (100%) rename examples/{toys => toybox/lights}/lightSwitch.js (97%) rename examples/{toys => toybox}/ping_pong_gun/createPingPongGun.js (100%) rename examples/{toys => toybox}/ping_pong_gun/createTargets.js (100%) rename examples/{toys => toybox}/ping_pong_gun/pingPongGun.js (100%) rename examples/{toys => toybox}/ping_pong_gun/wallTarget.js (100%) rename examples/{toys => toybox/spray_paint}/sprayPaintCan.js (98%) diff --git a/examples/toys/breakdanceCore.js b/examples/entityScripts/breakdanceCore.js similarity index 99% rename from examples/toys/breakdanceCore.js rename to examples/entityScripts/breakdanceCore.js index 655e55dfc6..0220962ec8 100644 --- a/examples/toys/breakdanceCore.js +++ b/examples/entityScripts/breakdanceCore.js @@ -1,6 +1,5 @@ // // breakdanceCore.js -// examples/toys // // This is the core breakdance game library, it can be used as part of an entity script, or an omniTool module, or bootstapped on it's own // Created by Brad Hefta-Gaub on August 24, 2015 diff --git a/examples/toys/breakdanceToy.js b/examples/entityScripts/breakdanceToy.js similarity index 100% rename from examples/toys/breakdanceToy.js rename to examples/entityScripts/breakdanceToy.js diff --git a/examples/toys/grenade.js b/examples/entityScripts/grenade.js similarity index 100% rename from examples/toys/grenade.js rename to examples/entityScripts/grenade.js diff --git a/examples/toys/magBalls.js b/examples/entityScripts/magBalls.js similarity index 97% rename from examples/toys/magBalls.js rename to examples/entityScripts/magBalls.js index e163aa5ffd..721e174676 100644 --- a/examples/toys/magBalls.js +++ b/examples/entityScripts/magBalls.js @@ -7,10 +7,10 @@ // // FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js -Script.include("../toys/magBalls/constants.js"); -Script.include("../toys/magBalls/graph.js"); -Script.include("../toys/magBalls/edgeSpring.js"); -Script.include("../toys/magBalls/magBalls.js"); +Script.include("../entityScripts/magBalls/constants.js"); +Script.include("../entityScripts/magBalls/graph.js"); +Script.include("../entityScripts/magBalls/edgeSpring.js"); +Script.include("../entityScripts/magBalls/magBalls.js"); Script.include("avatarRelativeOverlays.js"); OmniToolModuleType = "MagBallsController" diff --git a/examples/toys/magBalls/constants.js b/examples/entityScripts/magBalls/constants.js similarity index 100% rename from examples/toys/magBalls/constants.js rename to examples/entityScripts/magBalls/constants.js diff --git a/examples/toys/magBalls/debugUtils.js b/examples/entityScripts/magBalls/debugUtils.js similarity index 100% rename from examples/toys/magBalls/debugUtils.js rename to examples/entityScripts/magBalls/debugUtils.js diff --git a/examples/toys/magBalls/edgeSpring.js b/examples/entityScripts/magBalls/edgeSpring.js similarity index 100% rename from examples/toys/magBalls/edgeSpring.js rename to examples/entityScripts/magBalls/edgeSpring.js diff --git a/examples/toys/magBalls/graph.js b/examples/entityScripts/magBalls/graph.js similarity index 100% rename from examples/toys/magBalls/graph.js rename to examples/entityScripts/magBalls/graph.js diff --git a/examples/toys/magBalls/magBalls.js b/examples/entityScripts/magBalls/magBalls.js similarity index 100% rename from examples/toys/magBalls/magBalls.js rename to examples/entityScripts/magBalls/magBalls.js diff --git a/examples/toys/AC_scripts/flickeringLight.js b/examples/toybox/AC_scripts/flickeringLight.js similarity index 100% rename from examples/toys/AC_scripts/flickeringLight.js rename to examples/toybox/AC_scripts/flickeringLight.js diff --git a/examples/toys/AC_scripts/originalPositionResetter.js b/examples/toybox/AC_scripts/originalPositionResetter.js similarity index 100% rename from examples/toys/AC_scripts/originalPositionResetter.js rename to examples/toybox/AC_scripts/originalPositionResetter.js diff --git a/examples/toys/AC_scripts/toybox_sounds.js b/examples/toybox/AC_scripts/toybox_sounds.js similarity index 100% rename from examples/toys/AC_scripts/toybox_sounds.js rename to examples/toybox/AC_scripts/toybox_sounds.js diff --git a/examples/toys/basketball/createHoop.js b/examples/toybox/basketball/createHoop.js similarity index 100% rename from examples/toys/basketball/createHoop.js rename to examples/toybox/basketball/createHoop.js diff --git a/examples/toys/basketball/createRack.js b/examples/toybox/basketball/createRack.js similarity index 100% rename from examples/toys/basketball/createRack.js rename to examples/toybox/basketball/createRack.js diff --git a/examples/toys/basketball/createSingleBasketball.js b/examples/toybox/basketball/createSingleBasketball.js similarity index 100% rename from examples/toys/basketball/createSingleBasketball.js rename to examples/toybox/basketball/createSingleBasketball.js diff --git a/examples/toys/blockers/createTestBlocks.js b/examples/toybox/blockers/createTestBlocks.js similarity index 100% rename from examples/toys/blockers/createTestBlocks.js rename to examples/toybox/blockers/createTestBlocks.js diff --git a/examples/toys/bubblewand/createWand.js b/examples/toybox/bubblewand/createWand.js similarity index 100% rename from examples/toys/bubblewand/createWand.js rename to examples/toybox/bubblewand/createWand.js diff --git a/examples/toys/bubblewand/wand.js b/examples/toybox/bubblewand/wand.js similarity index 100% rename from examples/toys/bubblewand/wand.js rename to examples/toybox/bubblewand/wand.js diff --git a/examples/toys/cat.js b/examples/toybox/cat/cat.js similarity index 100% rename from examples/toys/cat.js rename to examples/toybox/cat/cat.js diff --git a/examples/toys/doll/createDoll.js b/examples/toybox/doll/createDoll.js similarity index 100% rename from examples/toys/doll/createDoll.js rename to examples/toybox/doll/createDoll.js diff --git a/examples/toys/doll/doll.js b/examples/toybox/doll/doll.js similarity index 100% rename from examples/toys/doll/doll.js rename to examples/toybox/doll/doll.js diff --git a/examples/toys/flashlight/createFlashlight.js b/examples/toybox/flashlight/createFlashlight.js similarity index 100% rename from examples/toys/flashlight/createFlashlight.js rename to examples/toybox/flashlight/createFlashlight.js diff --git a/examples/toys/flashlight/flashlight.js b/examples/toybox/flashlight/flashlight.js similarity index 100% rename from examples/toys/flashlight/flashlight.js rename to examples/toybox/flashlight/flashlight.js diff --git a/examples/toys/lightSwitch.js b/examples/toybox/lights/lightSwitch.js similarity index 97% rename from examples/toys/lightSwitch.js rename to examples/toybox/lights/lightSwitch.js index 5c47108c94..fbd314a9af 100644 --- a/examples/toys/lightSwitch.js +++ b/examples/toybox/lights/lightSwitch.js @@ -17,7 +17,7 @@ (function () { var _this; - var utilitiesScript = Script.resolvePath("../libraries/utils.js"); + var utilitiesScript = Script.resolvePath("../../libraries/utils.js"); Script.include(utilitiesScript); LightSwitch = function () { _this = this; diff --git a/examples/toys/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js similarity index 100% rename from examples/toys/ping_pong_gun/createPingPongGun.js rename to examples/toybox/ping_pong_gun/createPingPongGun.js diff --git a/examples/toys/ping_pong_gun/createTargets.js b/examples/toybox/ping_pong_gun/createTargets.js similarity index 100% rename from examples/toys/ping_pong_gun/createTargets.js rename to examples/toybox/ping_pong_gun/createTargets.js diff --git a/examples/toys/ping_pong_gun/pingPongGun.js b/examples/toybox/ping_pong_gun/pingPongGun.js similarity index 100% rename from examples/toys/ping_pong_gun/pingPongGun.js rename to examples/toybox/ping_pong_gun/pingPongGun.js diff --git a/examples/toys/ping_pong_gun/wallTarget.js b/examples/toybox/ping_pong_gun/wallTarget.js similarity index 100% rename from examples/toys/ping_pong_gun/wallTarget.js rename to examples/toybox/ping_pong_gun/wallTarget.js diff --git a/examples/toys/sprayPaintCan.js b/examples/toybox/spray_paint/sprayPaintCan.js similarity index 98% rename from examples/toys/sprayPaintCan.js rename to examples/toybox/spray_paint/sprayPaintCan.js index 1532ff19f3..4e6719af76 100644 --- a/examples/toys/sprayPaintCan.js +++ b/examples/toybox/spray_paint/sprayPaintCan.js @@ -13,7 +13,7 @@ // Script.include("../libraries/utils.js"); //Need absolute path for now, for testing before PR merge and s3 cloning. Will change post-merge - Script.include("../libraries/utils.js"); + Script.include("../../libraries/utils.js"); this.spraySound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/sprayPaintSound.wav"); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 19a7a5ee5e..e441db3aa6 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -14,14 +14,15 @@ var _this; - var sprayPaintScriptURL = Script.resolvePath("../examples/toys/sprayPaintCan.js"); - var catScriptURL = Script.resolvePath("../examples/toys/cat.js"); - var flashlightScriptURL = Script.resolvePath('../examples/toys/flashlight/flashlight.js'); - var pingPongScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/pingPongGun.js'); - var wandScriptURL = Script.resolvePath("../examples/toys/bubblewand/wand.js"); - var dollScriptURL = Script.resolvePath("../examples/toys/doll/doll.js"); - var lightsScriptURL = Script.resolvePath("../examples/toys/lightSwitch.js"); - var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTarget.js'); + var sprayPaintScriptURL = Script.resolvePath("../examples/toybox/spray_paint/sprayPaintCan.js"); + var catScriptURL = Script.resolvePath("../examples/toybox/cat/cat.js"); + var flashlightScriptURL = Script.resolvePath('../examples/toybox/flashlight/flashlight.js'); + var pingPongScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/pingPongGun.js'); + var wandScriptURL = Script.resolvePath("../examples/toybox/bubblewand/wand.js"); + var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); + var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); + var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); + ResetSwitch = function() { _this = this; diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 54f29b4259..956db41235 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -14,14 +14,14 @@ var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js"); Script.include(utilitiesScript); -var sprayPaintScriptURL = Script.resolvePath("../examples/toys/sprayPaintCan.js"); -var catScriptURL = Script.resolvePath("../examples/toys/cat.js"); -var flashlightScriptURL = Script.resolvePath('../examples/toys/flashlight/flashlight.js'); -var pingPongScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/pingPongGun.js'); -var wandScriptURL = Script.resolvePath("../examples/toys/bubblewand/wand.js"); -var dollScriptURL = Script.resolvePath("../examples/toys/doll/doll.js"); -var lightsScriptURL = Script.resolvePath("../examples/toys/lightSwitch.js"); -var targetsScriptURL = Script.resolvePath('../examples/toys/ping_pong_gun/wallTarget.js'); + var sprayPaintScriptURL = Script.resolvePath("../examples/toybox/spray_paint/sprayPaintCan.js"); + var catScriptURL = Script.resolvePath("../examples/toybox/cat/cat.js"); + var flashlightScriptURL = Script.resolvePath('../examples/toybox/flashlight/flashlight.js'); + var pingPongScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/pingPongGun.js'); + var wandScriptURL = Script.resolvePath("../examples/toybox/bubblewand/wand.js"); + var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); + var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); + var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); From c0eccca6d3554890a5ba14485c1a7273b6f935f9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 21 Oct 2015 12:29:26 -0700 Subject: [PATCH 0298/1003] relay 'kinematic' mode of a hold to other interfaces --- interface/src/avatar/AvatarActionHold.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index cf455ea98c..d2f5514e10 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -245,6 +245,8 @@ QByteArray AvatarActionHold::serialize() const { dataStream << _expires + getEntityServerClockSkew(); dataStream << _tag; + dataStream << _kinematic; + dataStream << _kinematicSetVelocity; }); return serializedActionArguments; @@ -278,6 +280,8 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) { dataStream >> _expires; _expires -= getEntityServerClockSkew(); dataStream >> _tag; + dataStream >> _kinematic; + dataStream >> _kinematicSetVelocity; #if WANT_DEBUG qDebug() << "deserialize AvatarActionHold: " << _holderID From 84e2ace0ead11d7de1e8a60a60dff127084a4bc4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 12:57:48 -0700 Subject: [PATCH 0299/1003] 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 0300/1003] 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 0301/1003] 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 0302/1003] 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 856af8c764b2fbad658d602bd7d0f6dc20b1d0b6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 21 Oct 2015 14:31:17 -0700 Subject: [PATCH 0303/1003] 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 b9b03bd842e03a60ba447c4039c2583b08bc1e33 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 14:31:22 -0700 Subject: [PATCH 0304/1003] 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 0305/1003] 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 0306/1003] 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 0307/1003] 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 0308/1003] 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 0309/1003] 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 c83af43d0e93d1b6f27417240abbc3326d975c32 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 21 Oct 2015 17:34:09 -0700 Subject: [PATCH 0310/1003] Added strafe and backward blending --- .../defaultAvatar_full/avatar-animation.json | 118 ++++++++++++++---- libraries/animation/src/Rig.cpp | 53 +++++--- libraries/animation/src/Rig.h | 3 +- 3 files changed, 133 insertions(+), 41 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index e75e806df6..723176c17e 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -4,7 +4,7 @@ "id": "ikOverlay", "type": "overlay", "data": { - "alpha": 0.0, + "alpha": 1.0, "boneSet": "fullBody" }, "children": [ @@ -532,8 +532,8 @@ "alpha": 0.0, "sync": true, "timeScale": 1.0, - "timeScaleVar": "walkTimeScale", - "alphaVar": "walkAlpha" + "timeScaleVar": "moveForwardTimeScale", + "alphaVar": "moveForwardAlpha" }, "children": [ { @@ -576,16 +576,40 @@ }, { "id": "walkBwd", - "type": "clip", + "type": "blendLinear", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", - "startFrame": 0.0, - "endFrame": 37.0, + "alpha": 1.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true, - "timeScaleVar": "walkTimeScale" + "timeScaleVar": "moveBackwardTimeScale", + "alphaVar": "moveBackwardAlpha" }, - "children": [] + "children": [ + { + "id": "walkBwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx", + "startFrame": 0.0, + "endFrame": 38.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkBwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", + "startFrame": 0.0, + "endFrame": 36.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "turnLeft", @@ -613,27 +637,77 @@ }, { "id": "strafeLeft", - "type": "clip", + "type": "blendLinear", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", - "startFrame": 0.0, - "endFrame": 30.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true + "timeScaleVar": "moveLateralTimeScale", + "alphaVar": "moveLateralAlpha" }, - "children": [] + "children": [ + { + "id": "strafeLeftShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafeLeftNormal", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { "id": "strafeRight", - "type": "clip", + "type": "blendLinear", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", - "startFrame": 0.0, - "endFrame": 30.0, + "alpha": 0.0, + "sync": true, "timeScale": 1.0, - "loopFlag": true + "timeScaleVar": "moveLateralTimeScale", + "alphaVar": "moveLateralAlpha" }, - "children": [] + "children": [ + { + "id": "strafeRightShort", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafeRightNormal", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] } ] } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9179e7675f..cf1d1bc703 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -408,25 +408,20 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return _jointStates[jointIndex].getTransform(); } -void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut) { +void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const { - // filter speed using a moving average. - _averageForwardSpeed.updateAverage(speed); - speed = _averageForwardSpeed.getAverage(); - - const int NUM_FWD_SPEEDS = 3; - float FWD_SPEEDS[NUM_FWD_SPEEDS] = { 0.3f, 1.4f, 2.7f }; // m/s + assert(referenceSpeeds.size() > 0); // first calculate alpha by lerping between speeds. float alpha = 0.0f; - if (speed <= FWD_SPEEDS[0]) { + if (speed <= referenceSpeeds.front()) { alpha = 0.0f; - } else if (speed > FWD_SPEEDS[NUM_FWD_SPEEDS - 1]) { - alpha = (float)(NUM_FWD_SPEEDS - 1); + } else if (speed > referenceSpeeds.back()) { + alpha = (float)(referenceSpeeds.size() - 1); } else { - for (int i = 0; i < NUM_FWD_SPEEDS - 1; i++) { - if (FWD_SPEEDS[i] < speed && speed < FWD_SPEEDS[i + 1]) { - alpha = (float)i + ((speed - FWD_SPEEDS[i]) / (FWD_SPEEDS[i + 1] - FWD_SPEEDS[i])); + for (size_t i = 0; i < referenceSpeeds.size() - 1; i++) { + if (referenceSpeeds[i] < speed && speed < referenceSpeeds[i + 1]) { + alpha = (float)i + ((speed - referenceSpeeds[i]) / (referenceSpeeds[i + 1] - referenceSpeeds[i])); break; } } @@ -436,13 +431,18 @@ void Rig::calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* // NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear. int prevIndex = glm::floor(alpha); int nextIndex = glm::ceil(alpha); - float animSpeed = lerp(FWD_SPEEDS[prevIndex], FWD_SPEEDS[nextIndex], (float)glm::fract(alpha)); + float animSpeed = lerp(referenceSpeeds[prevIndex], referenceSpeeds[nextIndex], (float)glm::fract(alpha)); float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed); *alphaOut = alpha; *timeScaleOut = timeScale; } +// animation reference speeds. +static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 2.5f }; // m/s +static const std::vector BACKWARD_SPEEDS = { 0.45f, 1.4f }; // m/s +static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s + void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -468,15 +468,32 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; + // filter speeds using a simple moving average. + _averageForwardSpeed.updateAverage(forwardSpeed); + _averageLateralSpeed.updateAverage(lateralSpeed); + // sine wave LFO var for testing. static float t = 0.0f; _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); - float walkAlpha, walkTimeScale; - calcWalkForwardAlphaAndTimeScale(glm::length(localVel), &walkAlpha, &walkTimeScale); + float moveForwardAlpha = 0.0f; + float moveForwardTimeScale = 1.0f; + float moveBackwardAlpha = 0.0f; + float moveBackwardTimeScale = 1.0f; + float moveLateralAlpha = 0.0f; + float moveLateralTimeScale = 1.0f; - _animVars.set("walkTimeScale", walkTimeScale); - _animVars.set("walkAlpha", walkAlpha); + // calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds. + calcAnimAlphaAndTimeScale(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha, &moveForwardTimeScale); + calcAnimAlphaAndTimeScale(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha, &moveBackwardTimeScale); + calcAnimAlphaAndTimeScale(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha, &moveLateralTimeScale); + + _animVars.set("moveFowardTimeScale", moveForwardTimeScale); + _animVars.set("moveForwardAlpha", moveForwardAlpha); + _animVars.set("moveBackwardTimeScale", moveBackwardTimeScale); + _animVars.set("moveBackwardAlpha", moveBackwardAlpha); + _animVars.set("moveLateralTimeScale", moveLateralTimeScale); + _animVars.set("moveLateralAlpha", moveLateralAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 93692eb00c..d1031c017a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,7 +208,7 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); - void calcWalkForwardAlphaAndTimeScale(float speed, float* alphaOut, float* timeScaleOut); + void calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const; QVector _jointStates; int _rootJointIndex = -1; @@ -245,6 +245,7 @@ public: float _rightHandOverlayAlpha = 0.0f; SimpleMovingAverage _averageForwardSpeed{ 25 }; + SimpleMovingAverage _averageLateralSpeed{ 25 }; }; #endif /* defined(__hifi__Rig__) */ From 637654adeacce63cdd0da4495ec5b609818c7a8f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 21 Oct 2015 18:40:13 -0700 Subject: [PATCH 0311/1003] 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 0312/1003] 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 0313/1003] 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 0314/1003] 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 0315/1003] 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 0316/1003] 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 0317/1003] 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 0318/1003] 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 695f0a75b0b0cd9bc9f0f4ff6ec6c9fb57f50ba7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 22 Oct 2015 11:12:51 -0700 Subject: [PATCH 0319/1003] fixes for builds with qt 5.5 --- libraries/render-utils/src/GlWindow.cpp | 6 ++-- libraries/ui/src/VrMenu.cpp | 40 +++++++++---------------- 2 files changed, 16 insertions(+), 30 deletions(-) diff --git a/libraries/render-utils/src/GlWindow.cpp b/libraries/render-utils/src/GlWindow.cpp index 78ac046b59..248b9305e2 100644 --- a/libraries/render-utils/src/GlWindow.cpp +++ b/libraries/render-utils/src/GlWindow.cpp @@ -43,11 +43,9 @@ bool GlWindow::makeCurrent() { qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); }); - #ifndef QT_NO_DEBUG - QOpenGLContext * currentContext = - #endif - QOpenGLContext::currentContext(); + QOpenGLContext * currentContext = QOpenGLContext::currentContext(); Q_ASSERT(_context == currentContext); + return makeCurrentResult; } diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index a2cc7f0214..57df4d5306 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -108,13 +108,10 @@ void VrMenu::addMenu(QMenu* menu) { Q_ASSERT(false); } QVariant returnedValue; - #ifndef QT_NO_DEBUG - bool invokeResult = - #endif - QMetaObject::invokeMethod(this, "addMenu", Qt::DirectConnection, - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, QVariant::fromValue(qmlParent)), - Q_ARG(QVariant, QVariant::fromValue(menu->title()))); + bool invokeResult = QMetaObject::invokeMethod(this, "addMenu", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, QVariant::fromValue(qmlParent)), + Q_ARG(QVariant, QVariant::fromValue(menu->title()))); Q_ASSERT(invokeResult); QObject* result = returnedValue.value(); Q_ASSERT(result); @@ -150,13 +147,11 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { QObject* menuQml = findMenuObject(userData->uuid.toString()); Q_ASSERT(menuQml); QVariant returnedValue; - #ifndef QT_NO_DEBUG - bool invokeResult = - #endif - QMetaObject::invokeMethod(this, "addItem", Qt::DirectConnection, - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, QVariant::fromValue(menuQml)), - Q_ARG(QVariant, QVariant::fromValue(action->text()))); + + bool invokeResult = QMetaObject::invokeMethod(this, "addItem", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, QVariant::fromValue(menuQml)), + Q_ARG(QVariant, QVariant::fromValue(action->text()))); Q_ASSERT(invokeResult); QObject* result = returnedValue.value(); Q_ASSERT(result); @@ -173,14 +168,11 @@ void VrMenu::insertAction(QAction* before, QAction* action) { } QObject* menu = beforeQml->parent(); QVariant returnedValue; - #ifndef QT_NO_DEBUG - bool invokeResult = - #endif - QMetaObject::invokeMethod(this, "insertItem", Qt::DirectConnection, - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, QVariant::fromValue(menu)), - Q_ARG(QVariant, QVariant::fromValue(beforeQml)), - Q_ARG(QVariant, QVariant::fromValue(action->text()))); + bool invokeResult = QMetaObject::invokeMethod(this, "insertItem", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, QVariant::fromValue(menu)), + Q_ARG(QVariant, QVariant::fromValue(beforeQml)), + Q_ARG(QVariant, QVariant::fromValue(action->text()))); Q_ASSERT(invokeResult); QObject* result = returnedValue.value(); Q_ASSERT(result); @@ -199,9 +191,5 @@ void VrMenu::removeAction(QAction* action) { bool invokeResult = QMetaObject::invokeMethod(this, "removeItem", Qt::DirectConnection, Q_ARG(QVariant, QVariant::fromValue(menu)), Q_ARG(QVariant, QVariant::fromValue(item))); -#ifndef QT_NO_DEBUG Q_ASSERT(invokeResult); -#else - Q_UNUSED(invokeResult); -#endif } From 67f2a0c6c803263171a98ab02588079beb1a4ba8 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 22 Oct 2015 11:39:51 -0700 Subject: [PATCH 0320/1003] 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 a44ac4cbfd13e54aa68bf101334a00f21f30dc45 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 22 Oct 2015 11:40:56 -0700 Subject: [PATCH 0321/1003] Fiing the dark colors for surfaces without texture --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 80327b7e54..30b72891dd 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -147,7 +147,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); } } else { - batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); + batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getWhiteTexture()); } // Normal map From b5ccd49959bbb2d8706e6edbafb8d9c5a9e90595 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 11:42:50 -0700 Subject: [PATCH 0322/1003] 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 0323/1003] 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 ea917f85f472332cc2ccc953cf1b10b17f36e09c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 22 Oct 2015 12:56:45 -0700 Subject: [PATCH 0324/1003] added ability to set keyhole radius on EntityViewer --- libraries/octree/src/OctreeHeadlessViewer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index 605db15cd2..cebfa5a928 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -46,6 +46,7 @@ public slots: // setters for camera attributes void setPosition(const glm::vec3& position) { _viewFrustum.setPosition(position); } void setOrientation(const glm::quat& orientation) { _viewFrustum.setOrientation(orientation); } + void setKeyholeRadius(float keyholdRadius) { _viewFrustum.setKeyholeRadius(keyholdRadius); } // setters for LOD and PPS void setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; } From 0d514ad6450f46da10d3d2a1b68becfc07f2fb12 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 13:02:08 -0700 Subject: [PATCH 0325/1003] Thread-safe avatar list access --- interface/src/avatar/AvatarManager.cpp | 8 +++++++- libraries/avatars/src/AvatarHashMap.cpp | 3 +++ libraries/avatars/src/AvatarHashMap.h | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5f0ac435e0..20fdfdb1e9 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -77,7 +77,10 @@ AvatarManager::AvatarManager(QObject* parent) : void AvatarManager::init() { _myAvatar->init(); - _avatarHash.insert(MY_AVATAR_KEY, _myAvatar); + { + QWriteLocker locker(&_hashLock); + _avatarHash.insert(MY_AVATAR_KEY, _myAvatar); + } connect(DependencyManager::get().data(), &SceneScriptingInterface::shouldRenderAvatarsChanged, this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection); @@ -127,6 +130,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } else if (avatar->shouldDie()) { removeAvatarMotionState(avatar); _avatarFades.push_back(avatarIterator.value()); + QWriteLocker locker(&_hashLock); avatarIterator = _avatarHash.erase(avatarIterator); } else { avatar->startUpdate(); @@ -202,6 +206,7 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) { if (avatar != _myAvatar && avatar->isInitialized()) { removeAvatarMotionState(avatar); _avatarFades.push_back(avatarIterator.value()); + QWriteLocker locker(&_hashLock); _avatarHash.erase(avatarIterator); } } @@ -218,6 +223,7 @@ void AvatarManager::clearOtherAvatars() { } else { removeAvatarMotionState(avatar); _avatarFades.push_back(avatarIterator.value()); + QWriteLocker locker(&_hashLock); avatarIterator = _avatarHash.erase(avatarIterator); } } diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 520bb34887..4256e2650e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -23,6 +23,7 @@ AvatarHashMap::AvatarHashMap() { } bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { + QReadLocker locker(&_hashLock); foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { glm::vec3 avatarPosition = sharedAvatar->getPosition(); float distance = glm::distance(avatarPosition, position); @@ -43,6 +44,7 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe AvatarSharedPointer avatar = newSharedAvatar(); avatar->setSessionUUID(sessionUUID); avatar->setOwningAvatarMixer(mixerWeakPointer); + QWriteLocker locker(&_hashLock); _avatarHash.insert(sessionUUID, avatar); return avatar; @@ -134,6 +136,7 @@ void AvatarHashMap::processKillAvatar(QSharedPointer packet, SharedNod } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID) { + QWriteLocker locker(&_hashLock); _avatarHash.remove(sessionUUID); } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 804233b76a..142fb1cab5 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -52,6 +52,9 @@ protected: virtual void removeAvatar(const QUuid& sessionUUID); AvatarHash _avatarHash; + // "Case-based safety": Most access to the _avatarHash is on the same thread. Write access is protected by a write lock. + // If you access from a different thread, it is your responsibility to write- or read-lock the _hashLock. + QReadWriteLock _hashLock; private: QUuid _lastOwnerSessionUUID; From 624ed7c71128cd413f577ca26963c648f7eb9fd8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 22 Oct 2015 13:14:38 -0700 Subject: [PATCH 0326/1003] fix comment --- libraries/avatars/src/AvatarHashMap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 142fb1cab5..4af741c8cc 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -52,8 +52,8 @@ protected: virtual void removeAvatar(const QUuid& sessionUUID); AvatarHash _avatarHash; - // "Case-based safety": Most access to the _avatarHash is on the same thread. Write access is protected by a write lock. - // If you access from a different thread, it is your responsibility to write- or read-lock the _hashLock. + // "Case-based safety": Most access to the _avatarHash is on the same thread. Write access is protected by a write-lock. + // If you read from a different thread, you must read-lock the _hashLock. (Scripted write access is not supported). QReadWriteLock _hashLock; private: From c826b48c92d189c80c6109183b018f2769a2ddf3 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 22 Oct 2015 15:05:59 -0700 Subject: [PATCH 0327/1003] 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 0328/1003] 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 0329/1003] 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 0330/1003] 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 0331/1003] 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 0332/1003] 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 0333/1003] 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 0334/1003] 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 0335/1003] 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 0336/1003] 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 0337/1003] 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 0338/1003] 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 0339/1003] 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 0340/1003] 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 0341/1003] 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 a66f31bb206ba7623179e9c4e0391bf5c870122a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 22 Oct 2015 16:33:31 -0700 Subject: [PATCH 0342/1003] Added AnimBlendLinearMove node AnimBlendLinearMove is now in use by forward, backward and strafe movements. Tuned rig moving average speeds to be more sensitive. --- .../defaultAvatar_full/avatar-animation.json | 42 +++--- .../animation/src/AnimBlendLinearMove.cpp | 126 ++++++++++++++++++ libraries/animation/src/AnimBlendLinearMove.h | 77 +++++++++++ libraries/animation/src/AnimNode.h | 1 + libraries/animation/src/AnimNodeLoader.cpp | 60 +++++++-- libraries/animation/src/Rig.cpp | 33 ++--- libraries/animation/src/Rig.h | 6 +- 7 files changed, 290 insertions(+), 55 deletions(-) create mode 100644 libraries/animation/src/AnimBlendLinearMove.cpp create mode 100644 libraries/animation/src/AnimBlendLinearMove.h diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 723176c17e..46c5cd9567 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -527,13 +527,13 @@ }, { "id": "walkFwd", - "type": "blendLinear", + "type": "blendLinearMove", "data": { "alpha": 0.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveForwardTimeScale", - "alphaVar": "moveForwardAlpha" + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.5, 1.4, 4.5], + "alphaVar": "moveForwardAlpha", + "desiredSpeedVar": "moveForwardSpeed" }, "children": [ { @@ -576,13 +576,13 @@ }, { "id": "walkBwd", - "type": "blendLinear", + "type": "blendLinearMove", "data": { - "alpha": 1.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveBackwardTimeScale", - "alphaVar": "moveBackwardAlpha" + "alpha": 0.0, + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.6, 1.45], + "alphaVar": "moveBackwardAlpha", + "desiredSpeedVar": "moveBackwardSpeed" }, "children": [ { @@ -637,13 +637,13 @@ }, { "id": "strafeLeft", - "type": "blendLinear", + "type": "blendLinearMove", "data": { "alpha": 0.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveLateralTimeScale", - "alphaVar": "moveLateralAlpha" + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.2, 0.65], + "alphaVar": "moveLateralAlpha", + "desiredSpeedVar": "moveLateralSpeed" }, "children": [ { @@ -674,13 +674,13 @@ }, { "id": "strafeRight", - "type": "blendLinear", + "type": "blendLinearMove", "data": { "alpha": 0.0, - "sync": true, - "timeScale": 1.0, - "timeScaleVar": "moveLateralTimeScale", - "alphaVar": "moveLateralAlpha" + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.2, 0.65], + "alphaVar": "moveLateralAlpha", + "desiredSpeedVar": "moveLateralSpeed" }, "children": [ { diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp new file mode 100644 index 0000000000..072dc28f57 --- /dev/null +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -0,0 +1,126 @@ +// +// AnimBlendLinearMove.cpp +// +// Created by Anthony J. Thibault on 10/22/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 "AnimBlendLinearMove.h" +#include "GLMHelpers.h" +#include "AnimationLogging.h" +#include "AnimUtil.h" +#include "AnimClip.h" + +AnimBlendLinearMove::AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector& characteristicSpeeds) : + AnimNode(AnimNode::Type::BlendLinearMove, id), + _alpha(alpha), + _desiredSpeed(desiredSpeed), + _characteristicSpeeds(characteristicSpeeds) { + +} + +AnimBlendLinearMove::~AnimBlendLinearMove() { + +} + +const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { + + assert(_children.size() == _characteristicSpeeds.size()); + + _alpha = animVars.lookup(_alphaVar, _alpha); + _desiredSpeed = animVars.lookup(_desiredSpeedVar, _desiredSpeed); + + if (_children.size() == 0) { + for (auto&& pose : _poses) { + pose = AnimPose::identity; + } + } else if (_children.size() == 1) { + const float alpha = 0.0f; + const int prevPoseIndex = 0; + const int nextPoseIndex = 0; + float prevDeltaTime, nextDeltaTime; + setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + } else { + + float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); + size_t prevPoseIndex = glm::floor(clampedAlpha); + size_t nextPoseIndex = glm::ceil(clampedAlpha); + float alpha = glm::fract(clampedAlpha); + float prevDeltaTime, nextDeltaTime; + setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + } + return _poses; +} + +// for AnimDebugDraw rendering +const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const { + return _poses; +} + +void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevDeltaTime, float nextDeltaTime) { + if (prevPoseIndex == nextPoseIndex) { + // this can happen if alpha is on an integer boundary + _poses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); + } else { + // need to eval and blend between two children. + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextDeltaTime, triggersOut); + + if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { + _poses.resize(prevPoses.size()); + + ::blend(_poses.size(), &prevPoses[0], &nextPoses[0], alpha, &_poses[0]); + } + } +} + +void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex, + float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) { + + const float FRAMES_PER_SECOND = 30.0f; + auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); + assert(prevClipNode); + auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); + assert(nextClipNode); + + float v0 = _characteristicSpeeds[prevPoseIndex]; + float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; + float v1 = _characteristicSpeeds[nextPoseIndex]; + float n1 = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; + + // rate of change in phase space, necessary to achive desired speed. + float omega = (_desiredSpeed * FRAMES_PER_SECOND) / ((1.0f - alpha) * v0 * n0 + alpha * v1 * n1); + + float f0 = prevClipNode->getStartFrame() + _phase * n0; + prevClipNode->setCurrentFrame(f0); + + float f1 = nextClipNode->getStartFrame() + _phase * n1; + nextClipNode->setCurrentFrame(f1); + + // integrate phase forward in time. + _phase += omega * dt; + + // detect loop trigger events + if (_phase >= 1.0f) { + triggersOut.push_back(_id + "Loop"); + _phase = glm::fract(_phase); + } + + *prevDeltaTimeOut = omega * dt * (n0 / FRAMES_PER_SECOND); + *nextDeltaTimeOut = omega * dt * (n1 / FRAMES_PER_SECOND); +} + +void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { + assert(_children.size() > 0); + auto clipNode = std::dynamic_pointer_cast(_children.front()); + assert(clipNode); + const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; + _phase = fmodf(frame, NUM_FRAMES); +} diff --git a/libraries/animation/src/AnimBlendLinearMove.h b/libraries/animation/src/AnimBlendLinearMove.h new file mode 100644 index 0000000000..4e04ce29cb --- /dev/null +++ b/libraries/animation/src/AnimBlendLinearMove.h @@ -0,0 +1,77 @@ +// +// AnimBlendLinearMove.h +// +// Created by Anthony J. Thibault on 10/22/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 +// + +#ifndef hifi_AnimBlendLinearMove_h +#define hifi_AnimBlendLinearMove_h + +#include "AnimNode.h" + +// Synced linear blend between two AnimNodes, where the playback speed of +// the animation is timeScaled to match movement speed. +// +// Each child animation is associated with a chracteristic speed. +// This defines the speed of that animation when played at the normal playback rate, 30 frames per second. +// +// The user also specifies a desired speed. This desired speed is used to timescale +// the animation to achive the desired movement velocity. +// +// Blending is determined by the alpha parameter. +// If the number of children is 2, then the alpha parameters should be between +// 0 and 1. The first animation will have a (1 - alpha) factor, and the second +// will have factor of alpha. +// +// This node supports more then 2 children. In this case the alpha should be +// between 0 and n - 1. This alpha can be used to linearly interpolate between +// the closest two children poses. This can be used to sweep through a series +// of animation poses. + +class AnimBlendLinearMove : public AnimNode { +public: + friend class AnimTests; + + AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector& characteristicSpeeds); + virtual ~AnimBlendLinearMove() override; + + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; + + void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } + void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; } + +protected: + // for AnimDebugDraw rendering + virtual const AnimPoseVec& getPosesInternal() const override; + + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, + size_t prevPoseIndex, size_t nextPoseIndex, + float prevDeltaTime, float nextDeltaTime); + + void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex, + float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut); + + virtual void setCurrentFrameInternal(float frame) override; + + AnimPoseVec _poses; + + float _alpha; + float _desiredSpeed; + + float _phase = 0.0f; + + QString _alphaVar; + QString _desiredSpeedVar; + + std::vector _characteristicSpeeds; + + // no copies + AnimBlendLinearMove(const AnimBlendLinearMove&) = delete; + AnimBlendLinearMove& operator=(const AnimBlendLinearMove&) = delete; +}; + +#endif // hifi_AnimBlendLinearMove_h diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 315ee357dd..ac3365c272 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -38,6 +38,7 @@ public: enum class Type { Clip = 0, BlendLinear, + BlendLinearMove, Overlay, StateMachine, Manipulator, diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index f9ebf2a630..38296923e3 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -16,6 +16,7 @@ #include "AnimNode.h" #include "AnimClip.h" #include "AnimBlendLinear.h" +#include "AnimBlendLinearMove.h" #include "AnimationLogging.h" #include "AnimOverlay.h" #include "AnimNodeLoader.h" @@ -29,6 +30,7 @@ using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& json // factory functions static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); +static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); @@ -36,17 +38,14 @@ static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, c // called after children have been loaded // returns node on success, nullptr on failure. -static bool processClipNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processBlendLinearNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } +static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); -static bool processManipulatorNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } -static bool processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; } static const char* animNodeTypeToString(AnimNode::Type type) { switch (type) { case AnimNode::Type::Clip: return "clip"; case AnimNode::Type::BlendLinear: return "blendLinear"; + case AnimNode::Type::BlendLinearMove: return "blendLinearMove"; case AnimNode::Type::Overlay: return "overlay"; case AnimNode::Type::StateMachine: return "stateMachine"; case AnimNode::Type::Manipulator: return "manipulator"; @@ -60,6 +59,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { switch (type) { case AnimNode::Type::Clip: return loadClipNode; case AnimNode::Type::BlendLinear: return loadBlendLinearNode; + case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode; case AnimNode::Type::Overlay: return loadOverlayNode; case AnimNode::Type::StateMachine: return loadStateMachineNode; case AnimNode::Type::Manipulator: return loadManipulatorNode; @@ -71,12 +71,13 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { switch (type) { - case AnimNode::Type::Clip: return processClipNode; - case AnimNode::Type::BlendLinear: return processBlendLinearNode; - case AnimNode::Type::Overlay: return processOverlayNode; + case AnimNode::Type::Clip: return processDoNothing; + case AnimNode::Type::BlendLinear: return processDoNothing; + case AnimNode::Type::BlendLinearMove: return processDoNothing; + case AnimNode::Type::Overlay: return processDoNothing; case AnimNode::Type::StateMachine: return processStateMachineNode; - case AnimNode::Type::Manipulator: return processManipulatorNode; - case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode; + case AnimNode::Type::Manipulator: return processDoNothing; + case AnimNode::Type::InverseKinematics: return processDoNothing; case AnimNode::Type::NumTypes: return nullptr; }; return nullptr; @@ -243,6 +244,45 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q return node; } +static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { + + READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); + READ_FLOAT(desiredSpeed, jsonObj, id, jsonUrl, nullptr); + + std::vector characteristicSpeeds; + auto speedsValue = jsonObj.value("characteristicSpeeds"); + if (!speedsValue.isArray()) { + qCCritical(animation) << "AnimNodeLoader, bad array \"characteristicSpeeds\" in blendLinearMove node, id =" << id << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + auto speedsArray = speedsValue.toArray(); + for (const auto& speedValue : speedsArray) { + if (!speedValue.isDouble()) { + qCCritical(animation) << "AnimNodeLoader, bad number in \"characteristicSpeeds\", id =" << id << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + float speedVal = (float)speedValue.toDouble(); + characteristicSpeeds.push_back(speedVal); + }; + + READ_OPTIONAL_STRING(alphaVar, jsonObj); + READ_OPTIONAL_STRING(desiredSpeedVar, jsonObj); + + auto node = std::make_shared(id, alpha, desiredSpeed, characteristicSpeeds); + + if (!alphaVar.isEmpty()) { + node->setAlphaVar(alphaVar); + } + + if (!desiredSpeedVar.isEmpty()) { + node->setDesiredSpeedVar(desiredSpeedVar); + } + + return node; +} + + static const char* boneSetStrings[AnimOverlay::NumBoneSets] = { "fullBody", "upperBody", diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index cf1d1bc703..7f9908faaf 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -408,11 +408,11 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return _jointStates[jointIndex].getTransform(); } -void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const { +void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { assert(referenceSpeeds.size() > 0); - // first calculate alpha by lerping between speeds. + // calculate alpha from linear combination of referenceSpeeds. float alpha = 0.0f; if (speed <= referenceSpeeds.front()) { alpha = 0.0f; @@ -427,20 +427,12 @@ void Rig::calcAnimAlphaAndTimeScale(float speed, const std::vector& refer } } - // now keeping the alpha fixed compute the timeScale. - // NOTE: This makes the assumption that the velocity of a linear blend between two animations is also linear. - int prevIndex = glm::floor(alpha); - int nextIndex = glm::ceil(alpha); - float animSpeed = lerp(referenceSpeeds[prevIndex], referenceSpeeds[nextIndex], (float)glm::fract(alpha)); - float timeScale = glm::clamp(0.5f, 2.0f, speed / animSpeed); - *alphaOut = alpha; - *timeScaleOut = timeScale; } // animation reference speeds. -static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 2.5f }; // m/s -static const std::vector BACKWARD_SPEEDS = { 0.45f, 1.4f }; // m/s +static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s +static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) { @@ -477,22 +469,21 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("sine", 2.0f * static_cast(0.5 * sin(t) + 0.5)); float moveForwardAlpha = 0.0f; - float moveForwardTimeScale = 1.0f; float moveBackwardAlpha = 0.0f; - float moveBackwardTimeScale = 1.0f; float moveLateralAlpha = 0.0f; - float moveLateralTimeScale = 1.0f; // calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds. - calcAnimAlphaAndTimeScale(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha, &moveForwardTimeScale); - calcAnimAlphaAndTimeScale(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha, &moveBackwardTimeScale); - calcAnimAlphaAndTimeScale(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha, &moveLateralTimeScale); + calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha); + calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha); + calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha); - _animVars.set("moveFowardTimeScale", moveForwardTimeScale); + _animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage()); _animVars.set("moveForwardAlpha", moveForwardAlpha); - _animVars.set("moveBackwardTimeScale", moveBackwardTimeScale); + + _animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage()); _animVars.set("moveBackwardAlpha", moveBackwardAlpha); - _animVars.set("moveLateralTimeScale", moveLateralTimeScale); + + _animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage())); _animVars.set("moveLateralAlpha", moveLateralAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index d1031c017a..861eba40b5 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,7 +208,7 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); - void calcAnimAlphaAndTimeScale(float speed, const std::vector& referenceSpeeds, float* alphaOut, float* timeScaleOut) const; + void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; QVector _jointStates; int _rootJointIndex = -1; @@ -244,8 +244,8 @@ public: float _leftHandOverlayAlpha = 0.0f; float _rightHandOverlayAlpha = 0.0f; - SimpleMovingAverage _averageForwardSpeed{ 25 }; - SimpleMovingAverage _averageLateralSpeed{ 25 }; + SimpleMovingAverage _averageForwardSpeed{ 10 }; + SimpleMovingAverage _averageLateralSpeed{ 10 }; }; #endif /* defined(__hifi__Rig__) */ From 86891704159f0154d48070a66a2ad3c8c55a84e6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 22 Oct 2015 16:40:53 -0700 Subject: [PATCH 0343/1003] Removed sync and timescale from AnimBlendLinear node. AnimBlendLinearMove will now be used instead. --- .../defaultAvatar_full/avatar-animation.json | 4 -- libraries/animation/src/AnimBlendLinear.cpp | 63 +------------------ libraries/animation/src/AnimBlendLinear.h | 15 +---- libraries/animation/src/AnimNodeLoader.cpp | 9 +-- 4 files changed, 4 insertions(+), 87 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 46c5cd9567..c6c67a21d5 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -190,8 +190,6 @@ "type": "blendLinear", "data": { "alpha": 0.0, - "sync": false, - "timeScale": 1.0, "alphaVar": "rightHandGrabBlend" }, "children": [ @@ -341,8 +339,6 @@ "type": "blendLinear", "data": { "alpha": 0.0, - "sync": false, - "timeScale": 1.0, "alphaVar": "leftHandGrabBlend" }, "children": [ diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 1f9026de9d..52c440a14e 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -14,11 +14,9 @@ #include "AnimUtil.h" #include "AnimClip.h" -AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale) : +AnimBlendLinear::AnimBlendLinear(const QString& id, float alpha) : AnimNode(AnimNode::Type::BlendLinear, id), - _alpha(alpha), - _sync(sync), - _timeScale(timeScale) { + _alpha(alpha) { } @@ -29,7 +27,6 @@ AnimBlendLinear::~AnimBlendLinear() { const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); - _timeScale = animVars.lookup(_timeScaleVar, _timeScale); if (_children.size() == 0) { for (auto&& pose : _poses) { @@ -44,10 +41,6 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, flo size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - if (_sync) { - setSyncAndAccumulateTime(dt, prevPoseIndex, nextPoseIndex, triggersOut); - } - evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; @@ -75,55 +68,3 @@ void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, T } } } - -void AnimBlendLinear::setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut) { - std::vector offsets(_children.size(), 0.0f); - std::vector timeScales(_children.size(), 1.0f); - - float lengthSum = 0.0f; - for (size_t i = 0; i < _children.size(); i++) { - // abort if we find a child that is NOT a clipNode. - if (_children[i]->getType() != AnimNode::Type::Clip) { - // TODO: FIXME: make sync this work for other node types. - return; - } - auto clipNode = std::dynamic_pointer_cast(_children[i]); - assert(clipNode); - if (clipNode) { - lengthSum += (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; - } - } - - _averageLength = lengthSum / (float)_children.size(); - - float progress = (_syncFrame / _averageLength); - - auto prevClipNode = std::dynamic_pointer_cast(_children[prevPoseIndex]); - float prevLength = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; - float prevOffset = prevClipNode->getStartFrame(); - float prevFrame = prevOffset + (progress * prevLength); - float prevTimeScale = _timeScale * (_averageLength / prevLength); - prevClipNode->setTimeScale(prevTimeScale); - prevClipNode->setCurrentFrame(prevFrame); - - auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); - float nextLength = (nextClipNode->getEndFrame() - nextClipNode->getStartFrame()) + 1.0f; - float nextOffset = nextClipNode->getStartFrame(); - float nextFrame = nextOffset + (progress * nextLength); - float nextTimeScale = _timeScale * (_averageLength / nextLength); - nextClipNode->setTimeScale(nextTimeScale); - nextClipNode->setCurrentFrame(nextFrame); - - const float START_FRAME = 0.0f; - const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, _syncFrame, dt, LOOP_FLAG, _id, triggersOut); -} - -void AnimBlendLinear::setCurrentFrameInternal(float frame) { - // because dt is 0, we should not encounter any triggers - const float dt = 0.0f; - Triggers triggers; - const float START_FRAME = 0.0f; - const bool LOOP_FLAG = true; - _syncFrame = ::accumulateTime(START_FRAME, _averageLength, _timeScale, frame, dt, LOOP_FLAG, _id, triggers); -} diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 7def6be91d..2478f9b473 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -22,21 +22,17 @@ // between 0 and n - 1. This alpha can be used to linearly interpolate between // the closest two children poses. This can be used to sweep through a series // of animation poses. -// -// The sync flag is used to synchronize between child animations of different lengths. -// Typically used to synchronize blending between walk and run cycles. class AnimBlendLinear : public AnimNode { public: friend class AnimTests; - AnimBlendLinear(const QString& id, float alpha, bool sync, float timeScale); + AnimBlendLinear(const QString& id, float alpha); virtual ~AnimBlendLinear() override; virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } - void setTimeScaleVar(const QString& timeScaleVar) { _timeScaleVar = timeScaleVar; } protected: // for AnimDebugDraw rendering @@ -44,21 +40,12 @@ protected: void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float dt); - void setSyncAndAccumulateTime(float dt, size_t prevPoseIndex, size_t nextPoseIndex, Triggers& triggersOut); - - virtual void setCurrentFrameInternal(float frame) override; AnimPoseVec _poses; float _alpha; - bool _sync; - float _timeScale; - - float _syncFrame = 0.0f; - float _averageLength = 0.0f; // average length of child animations in frames. QString _alphaVar; - QString _timeScaleVar; // no copies AnimBlendLinear(const AnimBlendLinear&) = delete; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 38296923e3..2a52e04e1d 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -225,22 +225,15 @@ static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr); - READ_BOOL(sync, jsonObj, id, jsonUrl, nullptr); - READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr); READ_OPTIONAL_STRING(alphaVar, jsonObj); - READ_OPTIONAL_STRING(timeScaleVar, jsonObj); - auto node = std::make_shared(id, alpha, sync, timeScale); + auto node = std::make_shared(id, alpha); if (!alphaVar.isEmpty()) { node->setAlphaVar(alphaVar); } - if (!timeScaleVar.isEmpty()) { - node->setTimeScaleVar(timeScaleVar); - } - return node; } From 92ddedd44b4d1487ea2e802351617136ab38cf0d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 22 Oct 2015 16:44:15 -0700 Subject: [PATCH 0344/1003] 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 0345/1003] 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 0346/1003] 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 0347/1003] 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 0348/1003] 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 0349/1003] 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 0350/1003] 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 e639d5313944f7b4689c4454f1a9b3f652cc897c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 23 Oct 2015 08:55:38 -0700 Subject: [PATCH 0351/1003] Use #include<> for headers not in current working directory. --- libraries/animation/src/AnimBlendLinearMove.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 072dc28f57..d8985f8b72 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -9,7 +9,7 @@ // #include "AnimBlendLinearMove.h" -#include "GLMHelpers.h" +#include #include "AnimationLogging.h" #include "AnimUtil.h" #include "AnimClip.h" From 6392b7080b5dfa7f908f0639cf36638ac2e13961 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 23 Oct 2015 10:36:32 -0700 Subject: [PATCH 0352/1003] move scripts no longer in toybox to better locations --- examples/{entityScripts => }/breakdanceCore.js | 0 examples/{entityScripts => }/breakdanceToy.js | 2 +- examples/entityScripts/breakdanceEntity.js | 2 +- examples/{entityScripts => }/grenade.js | 0 examples/{entityScripts => }/magBalls.js | 8 ++++---- examples/{entityScripts => }/magBalls/constants.js | 0 examples/{entityScripts => }/magBalls/debugUtils.js | 0 examples/{entityScripts => }/magBalls/edgeSpring.js | 0 examples/{entityScripts => }/magBalls/graph.js | 0 examples/{entityScripts => }/magBalls/magBalls.js | 0 10 files changed, 6 insertions(+), 6 deletions(-) rename examples/{entityScripts => }/breakdanceCore.js (100%) rename examples/{entityScripts => }/breakdanceToy.js (92%) rename examples/{entityScripts => }/grenade.js (100%) rename examples/{entityScripts => }/magBalls.js (97%) rename examples/{entityScripts => }/magBalls/constants.js (100%) rename examples/{entityScripts => }/magBalls/debugUtils.js (100%) rename examples/{entityScripts => }/magBalls/edgeSpring.js (100%) rename examples/{entityScripts => }/magBalls/graph.js (100%) rename examples/{entityScripts => }/magBalls/magBalls.js (100%) diff --git a/examples/entityScripts/breakdanceCore.js b/examples/breakdanceCore.js similarity index 100% rename from examples/entityScripts/breakdanceCore.js rename to examples/breakdanceCore.js diff --git a/examples/entityScripts/breakdanceToy.js b/examples/breakdanceToy.js similarity index 92% rename from examples/entityScripts/breakdanceToy.js rename to examples/breakdanceToy.js index 0190ab74e9..d133f49ab8 100644 --- a/examples/entityScripts/breakdanceToy.js +++ b/examples/breakdanceToy.js @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("../libraries/utils.js"); +Script.include("libraries/utils.js"); Script.include("breakdanceCore.js"); breakdanceStart(); Script.update.connect(breakdanceUpdate); diff --git a/examples/entityScripts/breakdanceEntity.js b/examples/entityScripts/breakdanceEntity.js index 5d4d418fec..8f8378c5d3 100644 --- a/examples/entityScripts/breakdanceEntity.js +++ b/examples/entityScripts/breakdanceEntity.js @@ -12,7 +12,7 @@ // (function() { - Script.include("../toys/breakdanceCore.js"); + Script.include("../breakdanceCore.js"); Script.include("../libraries/utils.js"); var _this; diff --git a/examples/entityScripts/grenade.js b/examples/grenade.js similarity index 100% rename from examples/entityScripts/grenade.js rename to examples/grenade.js diff --git a/examples/entityScripts/magBalls.js b/examples/magBalls.js similarity index 97% rename from examples/entityScripts/magBalls.js rename to examples/magBalls.js index 721e174676..c9b45164ab 100644 --- a/examples/entityScripts/magBalls.js +++ b/examples/magBalls.js @@ -7,10 +7,10 @@ // // FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js -Script.include("../entityScripts/magBalls/constants.js"); -Script.include("../entityScripts/magBalls/graph.js"); -Script.include("../entityScripts/magBalls/edgeSpring.js"); -Script.include("../entityScripts/magBalls/magBalls.js"); +Script.include("../magBalls/constants.js"); +Script.include("../magBalls/graph.js"); +Script.include("../magBalls/edgeSpring.js"); +Script.include("../magBalls/magBalls.js"); Script.include("avatarRelativeOverlays.js"); OmniToolModuleType = "MagBallsController" diff --git a/examples/entityScripts/magBalls/constants.js b/examples/magBalls/constants.js similarity index 100% rename from examples/entityScripts/magBalls/constants.js rename to examples/magBalls/constants.js diff --git a/examples/entityScripts/magBalls/debugUtils.js b/examples/magBalls/debugUtils.js similarity index 100% rename from examples/entityScripts/magBalls/debugUtils.js rename to examples/magBalls/debugUtils.js diff --git a/examples/entityScripts/magBalls/edgeSpring.js b/examples/magBalls/edgeSpring.js similarity index 100% rename from examples/entityScripts/magBalls/edgeSpring.js rename to examples/magBalls/edgeSpring.js diff --git a/examples/entityScripts/magBalls/graph.js b/examples/magBalls/graph.js similarity index 100% rename from examples/entityScripts/magBalls/graph.js rename to examples/magBalls/graph.js diff --git a/examples/entityScripts/magBalls/magBalls.js b/examples/magBalls/magBalls.js similarity index 100% rename from examples/entityScripts/magBalls/magBalls.js rename to examples/magBalls/magBalls.js From 71dfff7c3579c9395c03c1d356cd6535c011660b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 12:00:40 -0700 Subject: [PATCH 0353/1003] 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 0354/1003] 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 0355/1003] 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 0356/1003] 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 0357/1003] 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 0358/1003] 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 0359/1003] 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 0360/1003] 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 0361/1003] 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 0362/1003] 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 0363/1003] 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 0364/1003] 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 0365/1003] 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 0366/1003] 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 0367/1003] 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 7b0b77f4d11eb8b7f40631b111d0dafabfc56b68 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 23 Oct 2015 16:57:27 -0700 Subject: [PATCH 0368/1003] getAvatarHash => withAvatarHash --- interface/src/avatar/AvatarManager.cpp | 6 +- interface/src/avatar/MyAvatar.cpp | 107 ++++++++++++------------ libraries/avatars/src/AvatarHashMap.cpp | 4 + libraries/avatars/src/AvatarHashMap.h | 3 +- 4 files changed, 65 insertions(+), 55 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 20fdfdb1e9..4352934315 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -355,5 +355,9 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) if (sessionID == _myAvatar->getSessionUUID()) { return std::static_pointer_cast(_myAvatar); } - return getAvatarHash()[sessionID]; + AvatarSharedPointer avatar; + withAvatarHash([&avatar, &sessionID] (const AvatarHash& hash) { + avatar = hash[sessionID]; + }); + return avatar; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5920543dca..c6711c3324 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1018,71 +1018,72 @@ void MyAvatar::updateLookAtTargetAvatar() { const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; - foreach (const AvatarSharedPointer& avatarPointer, DependencyManager::get()->getAvatarHash()) { - auto avatar = static_pointer_cast(avatarPointer); - bool isCurrentTarget = avatar->getIsLookAtTarget(); - float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); - avatar->setIsLookAtTarget(false); - if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getScale())) { - float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); - if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { - _lookAtTargetAvatar = avatarPointer; - _targetAvatarPosition = avatarPointer->getPosition(); - smallestAngleTo = angleTo; - } - if (isLookingAtMe(avatar)) { + DependencyManager::get()->withAvatarHash([&] (const AvatarHash& hash) { + foreach (const AvatarSharedPointer& avatarPointer, hash) { + auto avatar = static_pointer_cast(avatarPointer); + bool isCurrentTarget = avatar->getIsLookAtTarget(); + float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); + avatar->setIsLookAtTarget(false); + if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getScale())) { + float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); + if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { + _lookAtTargetAvatar = avatarPointer; + _targetAvatarPosition = avatarPointer->getPosition(); + smallestAngleTo = angleTo; + } + if (isLookingAtMe(avatar)) { - // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. - glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. + // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. + glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. - // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) - // Let's get everything to world space: - glm::vec3 avatarLeftEye = getHead()->getLeftEyePosition(); - glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); - // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. - // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) - // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. - glm::mat4 leftEye = qApp->getEyeOffset(Eye::Left); - glm::mat4 rightEye = qApp->getEyeOffset(Eye::Right); - glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); - glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); - auto humanSystem = qApp->getViewFrustum(); - glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); - glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); + // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) + // Let's get everything to world space: + glm::vec3 avatarLeftEye = getHead()->getLeftEyePosition(); + glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); + // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. + // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) + // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. + glm::mat4 leftEye = qApp->getEyeOffset(Eye::Left); + glm::mat4 rightEye = qApp->getEyeOffset(Eye::Right); + glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); + glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); + auto humanSystem = qApp->getViewFrustum(); + glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); + glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); + // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. + // (We will be adding that offset to the camera position, after making some other adjustments.) + glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); - // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. - // (We will be adding that offset to the camera position, after making some other adjustments.) - glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); + // Scale by proportional differences between avatar and human. + float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye); + float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); + gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; - // Scale by proportional differences between avatar and human. - float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye); - float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); - gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; + // If the camera is also not oriented with the head, adjust by getting the offset in head-space... + /* Not needed (i.e., code is a no-op), but I'm leaving the example code here in case something like this is needed someday. + glm::quat avatarHeadOrientation = getHead()->getOrientation(); + glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; + // ... and treat that as though it were in camera space, bringing it back to world space. + // But camera is fudged to make the picture feel like the avatar's orientation. + glm::quat humanOrientation = humanSystem->getOrientation(); // or just avatar getOrienation() ? + gazeOffset = humanOrientation * gazeOffsetLocalToHead; + glm::vec3 corrected = humanSystem->getPosition() + gazeOffset; + */ - // If the camera is also not oriented with the head, adjust by getting the offset in head-space... - /* Not needed (i.e., code is a no-op), but I'm leaving the example code here in case something like this is needed someday. - glm::quat avatarHeadOrientation = getHead()->getOrientation(); - glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; - // ... and treat that as though it were in camera space, bringing it back to world space. - // But camera is fudged to make the picture feel like the avatar's orientation. - glm::quat humanOrientation = humanSystem->getOrientation(); // or just avatar getOrienation() ? - gazeOffset = humanOrientation * gazeOffsetLocalToHead; - glm::vec3 corrected = humanSystem->getPosition() + gazeOffset; - */ + // And now we can finally add that offset to the camera. + glm::vec3 corrected = qApp->getViewFrustum()->getPosition() + gazeOffset; - // And now we can finally add that offset to the camera. - glm::vec3 corrected = qApp->getViewFrustum()->getPosition() + gazeOffset; - - avatar->getHead()->setCorrectedLookAtPosition(corrected); + avatar->getHead()->setCorrectedLookAtPosition(corrected); + } else { + avatar->getHead()->clearCorrectedLookAtPosition(); + } } else { avatar->getHead()->clearCorrectedLookAtPosition(); } - } else { - avatar->getHead()->clearCorrectedLookAtPosition(); } - } + }); auto avatarPointer = _lookAtTargetAvatar.lock(); if (avatarPointer) { static_pointer_cast(avatarPointer)->setIsLookAtTarget(true); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 4256e2650e..3f1e668cd0 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -22,6 +22,10 @@ AvatarHashMap::AvatarHashMap() { connect(DependencyManager::get().data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } +void AvatarHashMap::withAvatarHash(std::function callback) { + QReadLocker locker(&_hashLock); + callback(_avatarHash); +} bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { QReadLocker locker(&_hashLock); foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 4af741c8cc..93364eb1ef 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -30,7 +31,7 @@ class AvatarHashMap : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - const AvatarHash& getAvatarHash() { return _avatarHash; } + void withAvatarHash(std::function); int size() { return _avatarHash.size(); } public slots: From 9285cf8e2cff33e2389a1b0cde10ed85b05cae49 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 17:12:26 -0700 Subject: [PATCH 0369/1003] 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 0370/1003] 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 0371/1003] 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 0372/1003] 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 0373/1003] 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 0374/1003] 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 0375/1003] 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 0376/1003] 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 0377/1003] 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 0378/1003] 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 0379/1003] 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 0380/1003] 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 0381/1003] 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 0382/1003] 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 0383/1003] 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 42ac8c5769962eb6c896f48c69b28fbc48406bda Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 23 Oct 2015 18:15:19 -0700 Subject: [PATCH 0384/1003] Stariting to reorganize the keyLight porperties of the Zone Entity --- .../entities/src/KeyLightPropertyGroup.cpp | 411 ++++++++++++++++++ .../entities/src/KeyLightPropertyGroup.h | 89 ++++ 2 files changed, 500 insertions(+) create mode 100644 libraries/entities/src/KeyLightPropertyGroup.cpp create mode 100644 libraries/entities/src/KeyLightPropertyGroup.h diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp new file mode 100644 index 0000000000..f9d8c07443 --- /dev/null +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -0,0 +1,411 @@ +// +// AnimationPropertyGroup.cpp +// libraries/entities/src +// +// Created by Brad Hefta-Gaub on 12/4/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 +// + +#include +#include + +#include + +#include "AnimationPropertyGroup.h" +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); + + if (_animationLoop) { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FPS, Animation, animation, FPS, fps, _animationLoop->getFPS); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame, _animationLoop->getFPS); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_PLAYING, Animation, animation, Running, running, _animationLoop->getRunning); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_LOOP, Animation, animation, Loop, loop, _animationLoop->getLoop); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame, _animationLoop->getFirstFrame); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame, _animationLoop->getLastFrame); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold, _animationLoop->getHold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically, _animationLoop->getStartAutomatically); + } else { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LOOP, Animation, animation, Loop, loop); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically); + } +} + +void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL); + + // legacy property support + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL); + COPY_PROPERTY_FROM_QSCRIPTVALUE_NOCHECK(animationSettings, QString, setFromOldAnimationSettings); + + if (_animationLoop) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, fps, float, _animationLoop->setFPS); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentFrame, float, _animationLoop->setCurrentFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, running, bool, _animationLoop->setRunning); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, loop, bool, _animationLoop->setLoop); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, _animationLoop->setFirstFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, _animationLoop->setLastFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, _animationLoop->setHold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, _animationLoop->setStartAutomatically); + + // legacy property support + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, _animationLoop->setFPS, _animationLoop->getFPS); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, _animationLoop->setRunning, _animationLoop->getRunning); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFrameIndex, float, _animationLoop->setCurrentFrame, _animationLoop->getCurrentFrame); + + } else { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, fps, float, setFPS); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentFrame, float, setCurrentFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, running, bool, setRunning); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, loop, bool, setLoop); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, setStartAutomatically); + + // legacy property support + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, setRunning, getRunning); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFrameIndex, float, setCurrentFrame, getCurrentFrame); + } + +} + +void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { + // the animations setting is a JSON string that may contain various animation settings. + // if it includes fps, currentFrame, or running, those values will be parsed out and + // will over ride the regular animation settings + + float fps = _animationLoop ? _animationLoop->getFPS() : getFPS(); + float currentFrame = _animationLoop ? _animationLoop->getCurrentFrame() : getCurrentFrame(); + bool running = _animationLoop ? _animationLoop->getRunning() : getRunning(); + float firstFrame = _animationLoop ? _animationLoop->getFirstFrame() : getFirstFrame(); + float lastFrame = _animationLoop ? _animationLoop->getLastFrame() : getLastFrame(); + bool loop = _animationLoop ? _animationLoop->getLoop() : getLoop(); + bool hold = _animationLoop ? _animationLoop->getHold() : getHold(); + bool startAutomatically = _animationLoop ? _animationLoop->getStartAutomatically() : getStartAutomatically(); + + QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); + QJsonObject settingsAsJsonObject = settingsAsJson.object(); + QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); + + if (settingsMap.contains("fps")) { + fps = settingsMap["fps"].toFloat(); + } + + // old settings had frameIndex + if (settingsMap.contains("frameIndex")) { + currentFrame = settingsMap["frameIndex"].toFloat(); + } + + if (settingsMap.contains("running")) { + running = settingsMap["running"].toBool(); + } + + if (settingsMap.contains("firstFrame")) { + firstFrame = settingsMap["firstFrame"].toFloat(); + } + + if (settingsMap.contains("lastFrame")) { + lastFrame = settingsMap["lastFrame"].toFloat(); + } + + if (settingsMap.contains("loop")) { + running = settingsMap["loop"].toBool(); + } + + if (settingsMap.contains("hold")) { + running = settingsMap["hold"].toBool(); + } + + if (settingsMap.contains("startAutomatically")) { + running = settingsMap["startAutomatically"].toBool(); + } + + if (_animationLoop) { + _animationLoop->setFPS(fps); + _animationLoop->setCurrentFrame(currentFrame); + _animationLoop->setRunning(running); + _animationLoop->setFirstFrame(firstFrame); + _animationLoop->setLastFrame(lastFrame); + _animationLoop->setLoop(loop); + _animationLoop->setHold(hold); + _animationLoop->setStartAutomatically(startAutomatically); + } else { + setFPS(fps); + setCurrentFrame(currentFrame); + setRunning(running); + setFirstFrame(firstFrame); + setLastFrame(lastFrame); + setLoop(loop); + setHold(hold); + setStartAutomatically(startAutomatically); + } +} + + +void AnimationPropertyGroup::debugDump() const { + qDebug() << " AnimationPropertyGroup: ---------------------------------------------"; + qDebug() << " url:" << getURL() << " has changed:" << urlChanged(); + qDebug() << " fps:" << getFPS() << " has changed:" << fpsChanged(); + qDebug() << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); +} + +void AnimationPropertyGroup::listChangedProperties(QList& out) { + if (urlChanged()) { + out << "animation-url"; + } + if (fpsChanged()) { + out << "animation-fps"; + } + if (currentFrameChanged()) { + out << "animation-currentFrame"; + } +} + + +bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); + if (_animationLoop) { + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, _animationLoop->getFPS()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, _animationLoop->getCurrentFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, _animationLoop->getRunning()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, _animationLoop->getLoop()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically()); + } else { + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, getLoop()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically()); + } + + return true; +} + + +bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); + + if (_animationLoop) { + READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, _animationLoop->setFPS); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, _animationLoop->setCurrentFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, _animationLoop->setRunning); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, _animationLoop->setLoop); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically); + } else { + READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, setLoop); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically); + } + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FPS, FPS); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FRAME_INDEX, CurrentFrame); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_PLAYING, Running); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LOOP, Loop); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void AnimationPropertyGroup::markAllChanged() { + _urlChanged = true; + _fpsChanged = true; + _currentFrameChanged = true; + _runningChanged = true; +} + +EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, url); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, fps); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, currentFrame); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, running); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LOOP, loop); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); + CHECK_PROPERTY_CHANGE(PROP_ANIMATION_START_AUTOMATICALLY, startAutomatically); + + return changedProperties; +} + +void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL); + if (_animationLoop) { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, _animationLoop->getFPS); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, _animationLoop->getCurrentFrame); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, _animationLoop->getRunning); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Loop, _animationLoop->getLoop); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, _animationLoop->getFirstFrame); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, _animationLoop->getLastFrame); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, _animationLoop->getHold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, _animationLoop->getStartAutomatically); + } else { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Loop, getLoop); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, getStartAutomatically); + } +} + +bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL); + if (_animationLoop) { + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, _animationLoop->setFPS); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, _animationLoop->setCurrentFrame); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, _animationLoop->setRunning); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Loop, loop, _animationLoop->setLoop); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, _animationLoop->setFirstFrame); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, _animationLoop->setLastFrame); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, _animationLoop->setHold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, _animationLoop->setStartAutomatically); + } else { + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Loop, loop, setLoop); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, setStartAutomatically); + } + + return somethingChanged; +} + +EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_ANIMATION_URL; + requestedProperties += PROP_ANIMATION_FPS; + requestedProperties += PROP_ANIMATION_FRAME_INDEX; + requestedProperties += PROP_ANIMATION_PLAYING; + requestedProperties += PROP_ANIMATION_LOOP; + requestedProperties += PROP_ANIMATION_FIRST_FRAME; + requestedProperties += PROP_ANIMATION_LAST_FRAME; + requestedProperties += PROP_ANIMATION_HOLD; + requestedProperties += PROP_ANIMATION_START_AUTOMATICALLY; + + return requestedProperties; +} + +void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); + if (_animationLoop) { + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, _animationLoop->getFPS()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, _animationLoop->getCurrentFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, _animationLoop->getRunning()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, _animationLoop->getLoop()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically()); + } else { + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, getLoop()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); + APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically()); + } +} + +int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); + + if (_animationLoop) { + // apply new properties to our associated AnimationLoop + READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, _animationLoop->setFPS); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, _animationLoop->setCurrentFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, _animationLoop->setRunning); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, _animationLoop->setLoop); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically); + } else { + READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, setLoop); + READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); + READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); + READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically); + } + + return bytesRead; +} diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h new file mode 100644 index 0000000000..92b4f7e118 --- /dev/null +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -0,0 +1,89 @@ +// +// KeyLightPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gateau on 2015/10/23. +// 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_KeyLightPropertyGroup_h +#define hifi_KeyLightPropertyGroup_h + +#include + +#include + +#include +#include "EntityItemPropertiesMacros.h" +#include "PropertyGroup.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; + +class KeyLightPropertyGroup : public PropertyGroup { +public: + void associateWithAnimationLoop(AnimationLoop* animationLoop) { _animationLoop = animationLoop; } + + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); + virtual void debugDump() const; + virtual void listChangedProperties(QList& out); + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes); + virtual void markAllChanged(); + virtual EntityPropertyFlags getChangedProperties() const; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties); + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged); + + DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); + DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f); + DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f); + DEFINE_PROPERTY(PROP_ANIMATION_PLAYING, Running, running, bool, false); // was animationIsPlaying + DEFINE_PROPERTY(PROP_ANIMATION_LOOP, Loop, loop, bool, true); // was animationSettings.loop + DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame + DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, AnimationLoop::MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame + DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold + DEFINE_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically, startAutomatically, bool, false); // was animationSettings.startAutomatically + +protected: + void setFromOldAnimationSettings(const QString& value); + + AnimationLoop* _animationLoop = nullptr; +}; + +#endif // hifi_KeyLightPropertyGroup_h From 7f3f202567b4ca36037673779c6db9bd3de8647e Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 23 Oct 2015 18:23:15 -0700 Subject: [PATCH 0385/1003] 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 06de0878020e1be466acef54dfd52b20219cbf89 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 23 Oct 2015 18:27:20 -0700 Subject: [PATCH 0386/1003] Filter out velocities computed from position delta if dt is small While in the HMD, updates can occur with very small deltaTime values. These this makes the position delta method of computing a velocity very susceptible to noise and precision errors. --- libraries/animation/src/Rig.cpp | 11 ++++++++++- libraries/animation/src/Rig.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7f9908faaf..bc47116c30 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -443,8 +443,14 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos // but some modes (e.g., hmd standing) update position without updating velocity. // It's very hard to debug hmd standing. (Look down at yourself, or have a second person observe. HMD third person is a bit undefined...) // So, let's create our own workingVelocity from the worldPosition... + glm::vec3 workingVelocity = _lastVelocity; glm::vec3 positionDelta = worldPosition - _lastPosition; - glm::vec3 workingVelocity = positionDelta / deltaTime; + + // don't trust position delta if deltaTime is 'small'. + const float SMALL_DELTA_TIME = 0.006f; // 6 ms + if (deltaTime > SMALL_DELTA_TIME) { + workingVelocity = positionDelta / deltaTime; + } #if !WANT_DEBUG // But for smoothest (non-hmd standing) results, go ahead and use velocity: @@ -453,9 +459,12 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } #endif + _lastVelocity = workingVelocity; + if (_enableAnimGraph) { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; + float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); float lateralSpeed = glm::dot(localVel, IDENTITY_RIGHT); float turningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 861eba40b5..42c59fac44 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -228,6 +228,7 @@ public: bool _enableAnimGraph = false; glm::vec3 _lastFront; glm::vec3 _lastPosition; + glm::vec3 _lastVelocity; std::shared_ptr _animNode; std::shared_ptr _animSkeleton; From 394511ac1bb4efca011134c351af104cf4fa44ce Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 24 Oct 2015 09:50:09 -0700 Subject: [PATCH 0387/1003] 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 0388/1003] 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 0389/1003] 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 0390/1003] 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 0391/1003] 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 0392/1003] 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 bc7ad9d3f78570b02dff9e9718897181b5f8b2f2 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 09:27:06 -0700 Subject: [PATCH 0393/1003] Move the keyLIght properties in their own group and add the ambientURL in --- .../src/EntityTreeRenderer.cpp | 9 +- .../entities/src/EntityItemProperties.cpp | 40 +- libraries/entities/src/EntityItemProperties.h | 5 +- libraries/entities/src/EntityPropertyFlags.h | 3 +- .../entities/src/KeyLightPropertyGroup.cpp | 428 +++++------------- .../entities/src/KeyLightPropertyGroup.h | 55 ++- libraries/entities/src/ZoneEntityItem.cpp | 56 +-- libraries/entities/src/ZoneEntityItem.h | 42 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 10 files changed, 207 insertions(+), 434 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index d5bf0bde8a..ee247b5b62 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -270,10 +270,11 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetStageYearTime(); _hasPreviousZone = true; } - scene->setKeyLightColor(zone->getKeyLightColorVec3()); - scene->setKeyLightIntensity(zone->getKeyLightIntensity()); - scene->setKeyLightAmbientIntensity(zone->getKeyLightAmbientIntensity()); - scene->setKeyLightDirection(zone->getKeyLightDirection()); + auto xcolor = zone->getKeyLightProperties().getColor(); + scene->setKeyLightColor(glm::vec3(xcolor.red / 256.0f, xcolor.green / 256.0f, xcolor.blue / 256.0f)); + scene->setKeyLightIntensity(zone->getKeyLightProperties().getIntensity()); + scene->setKeyLightAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity()); + scene->setKeyLightDirection(zone->getKeyLightProperties().getDirection()); scene->setStageSunModelEnable(zone->getStageProperties().getSunModelEnabled()); scene->setStageLocation(zone->getStageProperties().getLongitude(), zone->getStageProperties().getLatitude(), zone->getStageProperties().getAltitude()); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 19c8f779b4..002f1bb527 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -28,6 +28,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; AtmospherePropertyGroup EntityItemProperties::_staticAtmosphere; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; StagePropertyGroup EntityItemProperties::_staticStage; +KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); @@ -80,7 +81,8 @@ void EntityItemProperties::debugDump() const { getAnimation().debugDump(); getAtmosphere().debugDump(); getSkybox().debugDump(); - + getKeyLight().debugDump(); + qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); props.debugDumpBits(); @@ -235,10 +237,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RADIUS_FINISH, radiusFinish); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID); CHECK_PROPERTY_CHANGE(PROP_NAME, name); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, keyLightColor); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, keyLightIntensity); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, keyLightAmbientIntensity); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, keyLightDirection); CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -263,6 +261,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); changedProperties += _animation.getChangedProperties(); + changedProperties += _keyLight.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); changedProperties += _stage.getChangedProperties(); @@ -392,10 +391,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Zones only if (_type == EntityTypes::Zone) { - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, keyLightColor); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, keyLightIntensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_INTENSITY, keyLightAmbientIntensity); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, keyLightDirection); + _keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_MODE, backgroundMode, getBackgroundModeAsString()); _stage.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); @@ -546,10 +543,6 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightColor, xColor, setKeyLightColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightIntensity, float, setKeyLightIntensity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightAmbientIntensity, float, setKeyLightAmbientIntensity); - COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightDirection, glmVec3, setKeyLightDirection); COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); @@ -575,6 +568,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool } _animation.copyFromScriptValue(object, _defaultSettings); + _keyLight.copyFromScriptValue(object, _defaultSettings); _atmosphere.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); _stage.copyFromScriptValue(object, _defaultSettings); @@ -968,11 +962,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem } if (properties.getType() == EntityTypes::Zone) { - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, properties.getKeyLightColor()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, properties.getKeyLightIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, properties.getKeyLightAmbientIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, properties.getKeyLightDirection()); - + _staticKeyLight.setProperties(properties); + _staticKeyLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _staticStage.setProperties(properties); _staticStage.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -1252,11 +1244,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::Zone) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_COLOR, xColor, setKeyLightColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_INTENSITY, float, setKeyLightIntensity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setKeyLightAmbientIntensity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_DIRECTION, glm::vec3, setKeyLightDirection); - + properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); properties.getStage().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); @@ -1407,10 +1395,7 @@ void EntityItemProperties::markAllChanged() { _marketplaceIDChanged = true; - _keyLightColorChanged = true; - _keyLightIntensityChanged = true; - _keyLightAmbientIntensityChanged = true; - _keyLightDirectionChanged = true; + _keyLight.markAllChanged(); _backgroundModeChanged = true; @@ -1764,6 +1749,7 @@ QList EntityItemProperties::listChangedProperties() { } getAnimation().listChangedProperties(out); + getKeyLight().listChangedProperties(out); getAtmosphere().listChangedProperties(out); getSkybox().listChangedProperties(out); getStage().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 2227644484..b95f4d35f4 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -160,10 +160,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR); - DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY); - DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY); - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION); + DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index d70a5c9616..51a0c34c76 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -189,7 +189,8 @@ 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, + // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for // other properties which will never overlap with each other. PROP_SOURCE_URL = PROP_MODEL_URL, diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index f9d8c07443..b7e0a70b85 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -1,8 +1,8 @@ // -// AnimationPropertyGroup.cpp +// KeyLightPropertyGroup.h // libraries/entities/src // -// Created by Brad Hefta-Gaub on 12/4/13. +// Created by Sam Gateau on 2015/10/23. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -14,169 +14,71 @@ #include -#include "AnimationPropertyGroup.h" +#include "KeyLightPropertyGroup.h" #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); - if (_animationLoop) { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FPS, Animation, animation, FPS, fps, _animationLoop->getFPS); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame, _animationLoop->getFPS); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_PLAYING, Animation, animation, Running, running, _animationLoop->getRunning); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_LOOP, Animation, animation, Loop, loop, _animationLoop->getLoop); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame, _animationLoop->getFirstFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame, _animationLoop->getLastFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold, _animationLoop->getHold); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically, _animationLoop->getStartAutomatically); - } else { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LOOP, Animation, animation, Loop, loop); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_START_AUTOMATICALLY, Animation, animation, StartAutomatically, startAutomatically); - } +const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; +const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; +const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; +const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; + +void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLight, keyLight, AmbientIntensity, ambientIntensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_URL, KeyLight, keyLight, AmbientURL, ambientUrl); + } -void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL); +void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, ambientIntensity, float, setAmbientIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, glmVec3, setDirection); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, ambientURL, QString, setAmbientURL); + // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL); - COPY_PROPERTY_FROM_QSCRIPTVALUE_NOCHECK(animationSettings, QString, setFromOldAnimationSettings); - - if (_animationLoop) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, fps, float, _animationLoop->setFPS); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentFrame, float, _animationLoop->setCurrentFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, running, bool, _animationLoop->setRunning); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, loop, bool, _animationLoop->setLoop); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, _animationLoop->setFirstFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, _animationLoop->setLastFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, _animationLoop->setHold); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, _animationLoop->setStartAutomatically); - - // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, _animationLoop->setFPS, _animationLoop->getFPS); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, _animationLoop->setRunning, _animationLoop->getRunning); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFrameIndex, float, _animationLoop->setCurrentFrame, _animationLoop->getCurrentFrame); - - } else { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, fps, float, setFPS); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, currentFrame, float, setCurrentFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, running, bool, setRunning); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, loop, bool, setLoop); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, firstFrame, float, setFirstFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, lastFrame, float, setLastFrame); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, hold, bool, setHold); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, startAutomatically, bool, setStartAutomatically); - - // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFPS, float, setFPS, getFPS); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationIsPlaying, bool, setRunning, getRunning); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationFrameIndex, float, setCurrentFrame, getCurrentFrame); - } - + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, glmVec3, setDirection, getDirection); } -void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) { - // the animations setting is a JSON string that may contain various animation settings. - // if it includes fps, currentFrame, or running, those values will be parsed out and - // will over ride the regular animation settings - float fps = _animationLoop ? _animationLoop->getFPS() : getFPS(); - float currentFrame = _animationLoop ? _animationLoop->getCurrentFrame() : getCurrentFrame(); - bool running = _animationLoop ? _animationLoop->getRunning() : getRunning(); - float firstFrame = _animationLoop ? _animationLoop->getFirstFrame() : getFirstFrame(); - float lastFrame = _animationLoop ? _animationLoop->getLastFrame() : getLastFrame(); - bool loop = _animationLoop ? _animationLoop->getLoop() : getLoop(); - bool hold = _animationLoop ? _animationLoop->getHold() : getHold(); - bool startAutomatically = _animationLoop ? _animationLoop->getStartAutomatically() : getStartAutomatically(); +void KeyLightPropertyGroup::debugDump() const { + qDebug() << " KeyLightPropertyGroup: ---------------------------------------------"; + qDebug() << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2]; + qDebug() << " intensity:" << getIntensity(); + qDebug() << " direction:" << getDirection(); + qDebug() << " ambientIntensity:" << getAmbientIntensity(); + qDebug() << " ambientURL:" << getAmbientURL(); +} - QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); - QJsonObject settingsAsJsonObject = settingsAsJson.object(); - QVariantMap settingsMap = settingsAsJsonObject.toVariantMap(); - - if (settingsMap.contains("fps")) { - fps = settingsMap["fps"].toFloat(); +void KeyLightPropertyGroup::listChangedProperties(QList& out) { + if (colorChanged()) { + out << "keyLight-color"; } - - // old settings had frameIndex - if (settingsMap.contains("frameIndex")) { - currentFrame = settingsMap["frameIndex"].toFloat(); + if (intensityChanged()) { + out << "keyLight-intensity"; } - - if (settingsMap.contains("running")) { - running = settingsMap["running"].toBool(); + if (directionChanged()) { + out << "keyLight-direction"; } - - if (settingsMap.contains("firstFrame")) { - firstFrame = settingsMap["firstFrame"].toFloat(); + if (ambientIntensityChanged()) { + out << "keyLight-ambientIntensity"; } - - if (settingsMap.contains("lastFrame")) { - lastFrame = settingsMap["lastFrame"].toFloat(); - } - - if (settingsMap.contains("loop")) { - running = settingsMap["loop"].toBool(); - } - - if (settingsMap.contains("hold")) { - running = settingsMap["hold"].toBool(); - } - - if (settingsMap.contains("startAutomatically")) { - running = settingsMap["startAutomatically"].toBool(); - } - - if (_animationLoop) { - _animationLoop->setFPS(fps); - _animationLoop->setCurrentFrame(currentFrame); - _animationLoop->setRunning(running); - _animationLoop->setFirstFrame(firstFrame); - _animationLoop->setLastFrame(lastFrame); - _animationLoop->setLoop(loop); - _animationLoop->setHold(hold); - _animationLoop->setStartAutomatically(startAutomatically); - } else { - setFPS(fps); - setCurrentFrame(currentFrame); - setRunning(running); - setFirstFrame(firstFrame); - setLastFrame(lastFrame); - setLoop(loop); - setHold(hold); - setStartAutomatically(startAutomatically); + if (ambientURLChanged()) { + out << "keyLight-ambientURL"; } } -void AnimationPropertyGroup::debugDump() const { - qDebug() << " AnimationPropertyGroup: ---------------------------------------------"; - qDebug() << " url:" << getURL() << " has changed:" << urlChanged(); - qDebug() << " fps:" << getFPS() << " has changed:" << fpsChanged(); - qDebug() << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); -} - -void AnimationPropertyGroup::listChangedProperties(QList& out) { - if (urlChanged()) { - out << "animation-url"; - } - if (fpsChanged()) { - out << "animation-fps"; - } - if (currentFrameChanged()) { - out << "animation-currentFrame"; - } -} - - -bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, +bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, @@ -184,70 +86,36 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); - if (_animationLoop) { - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, _animationLoop->getFPS()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, _animationLoop->getCurrentFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, _animationLoop->getRunning()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, _animationLoop->getLoop()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically()); - } else { - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, getLoop()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically()); - } - + + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getAmbientIntensity()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, getAmbientURL()); + return true; } -bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { +bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { int bytesRead = 0; bool overwriteLocalData = true; bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); - - if (_animationLoop) { - READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, _animationLoop->setFPS); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, _animationLoop->setCurrentFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, _animationLoop->setRunning); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, _animationLoop->setLoop); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically); - } else { - READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, setLoop); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically); - } - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_URL, URL); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FPS, FPS); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FRAME_INDEX, CurrentFrame); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_PLAYING, Running); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LOOP, Loop); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically); - + + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setAmbientIntensity); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, QString, setAmbientURL); + + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_DIRECTION, Direction); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL); + processedBytes += bytesRead; Q_UNUSED(somethingChanged); @@ -255,96 +123,60 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF return true; } -void AnimationPropertyGroup::markAllChanged() { - _urlChanged = true; - _fpsChanged = true; - _currentFrameChanged = true; - _runningChanged = true; +void KeyLightPropertyGroup::markAllChanged() { + _colorChanged = true; + _intensityChanged = true; + _ambientIntensityChanged = true; + _directionChanged = true; + _ambientURLChanged = true; } -EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { +EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, color); + CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, intensity); + CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, ambientIntensity); + CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, direction); + CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_URL, ambientURL); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, url); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FPS, fps); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, currentFrame); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, running); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LOOP, loop); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); - CHECK_PROPERTY_CHANGE(PROP_ANIMATION_START_AUTOMATICALLY, startAutomatically); - return changedProperties; } -void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL); - if (_animationLoop) { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, _animationLoop->getFPS); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, _animationLoop->getCurrentFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, _animationLoop->getRunning); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Loop, _animationLoop->getLoop); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, _animationLoop->getFirstFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, _animationLoop->getLastFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, _animationLoop->getHold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, _animationLoop->getStartAutomatically); - } else { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Loop, getLoop); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FirstFrame, getFirstFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, LastFrame, getLastFrame); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Hold, getHold); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, StartAutomatically, getStartAutomatically); - } +void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const { + + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Color, getColor); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Intensity, getIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, AmbientIntensity, getAmbientIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Direction, getDirection); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, AmbientURL, getAmbientURL); } -bool AnimationPropertyGroup::setProperties(const EntityItemProperties& properties) { +bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL); - if (_animationLoop) { - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, _animationLoop->setFPS); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, _animationLoop->setCurrentFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, _animationLoop->setRunning); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Loop, loop, _animationLoop->setLoop); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, _animationLoop->setFirstFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, _animationLoop->setLastFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, _animationLoop->setHold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, _animationLoop->setStartAutomatically); - } else { - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Loop, loop, setLoop); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FirstFrame, firstFrame, setFirstFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, LastFrame, lastFrame, setLastFrame); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Hold, hold, setHold); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, StartAutomatically, startAutomatically, setStartAutomatically); - } + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Color, color, setColor); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Intensity, intensity, setIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, AmbientIntensity, ambientIntensity, setAmbientIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Direction, direction, setDirection); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, AmbientURL, ambientURL, setAmbientURL); return somethingChanged; } -EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { +EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties; - requestedProperties += PROP_ANIMATION_URL; - requestedProperties += PROP_ANIMATION_FPS; - requestedProperties += PROP_ANIMATION_FRAME_INDEX; - requestedProperties += PROP_ANIMATION_PLAYING; - requestedProperties += PROP_ANIMATION_LOOP; - requestedProperties += PROP_ANIMATION_FIRST_FRAME; - requestedProperties += PROP_ANIMATION_LAST_FRAME; - requestedProperties += PROP_ANIMATION_HOLD; - requestedProperties += PROP_ANIMATION_START_AUTOMATICALLY; + requestedProperties += PROP_KEYLIGHT_COLOR; + requestedProperties += PROP_KEYLIGHT_INTENSITY; + requestedProperties += PROP_KEYLIGHT_AMBIENT_INTENSITY; + requestedProperties += PROP_KEYLIGHT_DIRECTION; + requestedProperties += PROP_KEYLIGHT_AMBIENT_URL; return requestedProperties; } -void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, +void KeyLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, @@ -354,58 +186,26 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); - if (_animationLoop) { - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, _animationLoop->getFPS()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, _animationLoop->getCurrentFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, _animationLoop->getRunning()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, _animationLoop->getLoop()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, _animationLoop->getFirstFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, _animationLoop->getLastFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, _animationLoop->getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, _animationLoop->getStartAutomatically()); - } else { - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, getLoop()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, getFirstFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, getLastFrame()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, getHold()); - APPEND_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, getStartAutomatically()); - } + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getAmbientIntensity()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); + APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, getAmbientURL()); } -int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { int bytesRead = 0; const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); - - if (_animationLoop) { - // apply new properties to our associated AnimationLoop - READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, _animationLoop->setFPS); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, _animationLoop->setCurrentFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, _animationLoop->setRunning); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, _animationLoop->setLoop); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, _animationLoop->setFirstFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, _animationLoop->setLastFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, _animationLoop->setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, _animationLoop->setStartAutomatically); - } else { - READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LOOP, bool, setLoop); - READ_ENTITY_PROPERTY(PROP_ANIMATION_FIRST_FRAME, float, setFirstFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_LAST_FRAME, float, setLastFrame); - READ_ENTITY_PROPERTY(PROP_ANIMATION_HOLD, bool, setHold); - READ_ENTITY_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, bool, setStartAutomatically); - } + + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setAmbientIntensity); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, QString, setAmbientURL); return bytesRead; } diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index 92b4f7e118..d4ead9b733 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -29,7 +29,7 @@ class ReadBitstreamToTreeParams; class KeyLightPropertyGroup : public PropertyGroup { public: - void associateWithAnimationLoop(AnimationLoop* animationLoop) { _animationLoop = animationLoop; } + //void associateWithAnimationLoop(AnimationLoop* animationLoop) { _animationLoop = animationLoop; } // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; @@ -69,8 +69,20 @@ public: ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged); + + static const xColor DEFAULT_KEYLIGHT_COLOR; + static const float DEFAULT_KEYLIGHT_INTENSITY; + static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; + static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; - DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); + DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); + DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_KEYLIGHT_AMBIENT_INTENSITY); + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL, ambientURL, QString, ""); + + + /*DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f); DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f); DEFINE_PROPERTY(PROP_ANIMATION_PLAYING, Running, running, bool, false); // was animationIsPlaying @@ -79,11 +91,42 @@ public: DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, AnimationLoop::MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold DEFINE_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically, startAutomatically, bool, false); // was animationSettings.startAutomatically - + + xColor getKeyLightColor() const { xColor color = { _keyLightColor[RED_INDEX], _keyLightColor[GREEN_INDEX], _keyLightColor[BLUE_INDEX] }; return color; } + void setKeyLightColor(const xColor& value) { + _keyLightColor[RED_INDEX] = value.red; + _keyLightColor[GREEN_INDEX] = value.green; + _keyLightColor[BLUE_INDEX] = value.blue; + } + + void setKeyLightColor(const rgbColor& value) { + _keyLightColor[RED_INDEX] = value[RED_INDEX]; + _keyLightColor[GREEN_INDEX] = value[GREEN_INDEX]; + _keyLightColor[BLUE_INDEX] = value[BLUE_INDEX]; + } + + glm::vec3 getKeyLightColorVec3() const { + const quint8 MAX_COLOR = 255; + glm::vec3 color = { (float)_keyLightColor[RED_INDEX] / (float)MAX_COLOR, + (float)_keyLightColor[GREEN_INDEX] / (float)MAX_COLOR, + (float)_keyLightColor[BLUE_INDEX] / (float)MAX_COLOR }; + return color; + } + + + float getKeyLightIntensity() const { return _keyLightIntensity; } + void setKeyLightIntensity(float value) { _keyLightIntensity = value; } + + float getKeyLightAmbientIntensity() const { return _keyLightAmbientIntensity; } + void setKeyLightAmbientIntensity(float value) { _keyLightAmbientIntensity = value; } + + const glm::vec3& getKeyLightDirection() const { return _keyLightDirection; } + void setKeyLightDirection(const glm::vec3& value) { _keyLightDirection = value; } + */ + protected: - void setFromOldAnimationSettings(const QString& value); - - AnimationLoop* _animationLoop = nullptr; + // void setFromOldAnimationSettings(const QString& value); + }; #endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 3147af35e8..2d0e534fa7 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -23,10 +23,6 @@ bool ZoneEntityItem::_zonesArePickable = false; bool ZoneEntityItem::_drawZoneBoundaries = false; -const xColor ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; -const float ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; -const float ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; -const glm::vec3 ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX; const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = ""; @@ -39,13 +35,6 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID, const EntityIte { _type = EntityTypes::Zone; - _keyLightColor[RED_INDEX] = DEFAULT_KEYLIGHT_COLOR.red; - _keyLightColor[GREEN_INDEX] = DEFAULT_KEYLIGHT_COLOR.green; - _keyLightColor[BLUE_INDEX] = DEFAULT_KEYLIGHT_COLOR.blue; - - _keyLightIntensity = DEFAULT_KEYLIGHT_INTENSITY; - _keyLightAmbientIntensity = DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; - _keyLightDirection = DEFAULT_KEYLIGHT_DIRECTION; _shapeType = DEFAULT_SHAPE_TYPE; _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; @@ -77,11 +66,9 @@ EnvironmentData ZoneEntityItem::getEnvironmentData() const { EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightColor, getKeyLightColor); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightIntensity, getKeyLightIntensity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightAmbientIntensity, getKeyLightAmbientIntensity); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightDirection, getKeyLightDirection); - + + _keyLightProperties.getProperties(properties); + _stageProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); @@ -98,11 +85,8 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightColor, setKeyLightColor); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightIntensity, setKeyLightIntensity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightAmbientIntensity, setKeyLightAmbientIntensity); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightDirection, setKeyLightDirection); - + bool somethingChangedInKeyLight = _keyLightProperties.setProperties(properties); + bool somethingChangedInStage = _stageProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType); @@ -112,7 +96,7 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChangedInAtmosphere = _atmosphereProperties.setProperties(properties); bool somethingChangedInSkybox = _skyboxProperties.setProperties(properties); - somethingChanged = somethingChanged || somethingChangedInStage || somethingChangedInAtmosphere || somethingChangedInSkybox; + somethingChanged = somethingChanged || somethingChangedInKeyLight || somethingChangedInStage || somethingChangedInAtmosphere || somethingChangedInSkybox; if (somethingChanged) { bool wantDebug = false; @@ -135,14 +119,15 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, rgbColor, setKeyLightColor); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setKeyLightIntensity); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setKeyLightAmbientIntensity); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setKeyLightDirection); + int bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, somethingChanged); + + bytesRead += bytesFromKeylight; + dataAt += bytesFromKeylight; int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); - + bytesRead += bytesFromStage; dataAt += bytesFromStage; @@ -169,10 +154,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - requestedProperties += PROP_KEYLIGHT_COLOR; - requestedProperties += PROP_KEYLIGHT_INTENSITY; - requestedProperties += PROP_KEYLIGHT_AMBIENT_INTENSITY; - requestedProperties += PROP_KEYLIGHT_DIRECTION; + requestedProperties += _keyLightProperties.getEntityProperties(params); + requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_BACKGROUND_MODE; @@ -193,10 +176,8 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, _keyLightColor); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getKeyLightIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getKeyLightAmbientIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getKeyLightDirection()); + _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); _stageProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -217,15 +198,12 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " keyLightColor:" << _keyLightColor[0] << "," << _keyLightColor[1] << "," << _keyLightColor[2]; qCDebug(entities) << " position:" << debugTreeVector(getPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _keyLightIntensity:" << _keyLightIntensity; - qCDebug(entities) << " _keyLightAmbientIntensity:" << _keyLightAmbientIntensity; - qCDebug(entities) << " _keyLightDirection:" << _keyLightDirection; qCDebug(entities) << " _backgroundMode:" << EntityItemProperties::getBackgroundModeString(_backgroundMode); + _keyLightProperties.debugDump(); _stageProperties.debugDump(); _atmosphereProperties.debugDump(); _skyboxProperties.debugDump(); diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index e7f2e03981..c076e003e4 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -14,6 +14,7 @@ #include +#include "KeyLightPropertyGroup.h" #include "AtmospherePropertyGroup.h" #include "EntityItem.h" #include "EntityTree.h" @@ -48,36 +49,7 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged); - xColor getKeyLightColor() const { xColor color = { _keyLightColor[RED_INDEX], _keyLightColor[GREEN_INDEX], _keyLightColor[BLUE_INDEX] }; return color; } - void setKeyLightColor(const xColor& value) { - _keyLightColor[RED_INDEX] = value.red; - _keyLightColor[GREEN_INDEX] = value.green; - _keyLightColor[BLUE_INDEX] = value.blue; - } - void setKeyLightColor(const rgbColor& value) { - _keyLightColor[RED_INDEX] = value[RED_INDEX]; - _keyLightColor[GREEN_INDEX] = value[GREEN_INDEX]; - _keyLightColor[BLUE_INDEX] = value[BLUE_INDEX]; - } - - glm::vec3 getKeyLightColorVec3() const { - const quint8 MAX_COLOR = 255; - glm::vec3 color = { (float)_keyLightColor[RED_INDEX] / (float)MAX_COLOR, - (float)_keyLightColor[GREEN_INDEX] / (float)MAX_COLOR, - (float)_keyLightColor[BLUE_INDEX] / (float)MAX_COLOR }; - return color; - } - - - float getKeyLightIntensity() const { return _keyLightIntensity; } - void setKeyLightIntensity(float value) { _keyLightIntensity = value; } - - float getKeyLightAmbientIntensity() const { return _keyLightAmbientIntensity; } - void setKeyLightAmbientIntensity(float value) { _keyLightAmbientIntensity = value; } - - const glm::vec3& getKeyLightDirection() const { return _keyLightDirection; } - void setKeyLightDirection(const glm::vec3& value) { _keyLightDirection = value; } static bool getZonesArePickable() { return _zonesArePickable; } static void setZonesArePickable(bool value) { _zonesArePickable = value; } @@ -93,6 +65,8 @@ public: const QString getCompoundShapeURL() const { return _compoundShapeURL; } virtual void setCompoundShapeURL(const QString& url); + const KeyLightPropertyGroup& getKeyLightProperties() const { return _keyLightProperties; } + void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; } BackgroundMode getBackgroundMode() const { return _backgroundMode; } @@ -109,19 +83,11 @@ public: virtual void debugDump() const; - static const xColor DEFAULT_KEYLIGHT_COLOR; - static const float DEFAULT_KEYLIGHT_INTENSITY; - static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; - static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; static const ShapeType DEFAULT_SHAPE_TYPE; static const QString DEFAULT_COMPOUND_SHAPE_URL; protected: - // properties of the "sun" in the zone - rgbColor _keyLightColor; - float _keyLightIntensity; - float _keyLightAmbientIntensity; - glm::vec3 _keyLightDirection; + KeyLightPropertyGroup _keyLightProperties; ShapeType _shapeType = DEFAULT_SHAPE_TYPE; QString _compoundShapeURL; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 3c1d33deaf..7062942c51 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_ANIMATION_PROPERTIES_GROUP; + return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3654d5b5fa..64e0a9d8e4 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -143,5 +143,6 @@ const PacketVersion VERSION_ENTITIES_PROTOCOL_HEADER_SWAP = 43; 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; #endif // hifi_PacketHeaders_h From 900b07fdeef094f5e6ddf14939fc1a76934ae628 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 26 Oct 2015 10:00:07 -0700 Subject: [PATCH 0394/1003] 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 0395/1003] 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 ca30ce1ff0fc62e8ebc50df5585d92285a929a88 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 10:08:16 -0700 Subject: [PATCH 0396/1003] Move the keyLIght properties in their own group and add the ambientURL in --- .../entities/src/KeyLightPropertyGroup.h | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index d4ead9b733..b905a5a997 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -29,8 +29,6 @@ class ReadBitstreamToTreeParams; class KeyLightPropertyGroup : public PropertyGroup { public: - //void associateWithAnimationLoop(AnimationLoop* animationLoop) { _animationLoop = animationLoop; } - // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings); @@ -81,51 +79,7 @@ public: DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL, ambientURL, QString, ""); - - /*DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, URL, url, QString, ""); - DEFINE_PROPERTY(PROP_ANIMATION_FPS, FPS, fps, float, 30.0f); - DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, CurrentFrame, currentFrame, float, 0.0f); - DEFINE_PROPERTY(PROP_ANIMATION_PLAYING, Running, running, bool, false); // was animationIsPlaying - DEFINE_PROPERTY(PROP_ANIMATION_LOOP, Loop, loop, bool, true); // was animationSettings.loop - DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame - DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, AnimationLoop::MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame - DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold - DEFINE_PROPERTY(PROP_ANIMATION_START_AUTOMATICALLY, StartAutomatically, startAutomatically, bool, false); // was animationSettings.startAutomatically - - xColor getKeyLightColor() const { xColor color = { _keyLightColor[RED_INDEX], _keyLightColor[GREEN_INDEX], _keyLightColor[BLUE_INDEX] }; return color; } - void setKeyLightColor(const xColor& value) { - _keyLightColor[RED_INDEX] = value.red; - _keyLightColor[GREEN_INDEX] = value.green; - _keyLightColor[BLUE_INDEX] = value.blue; - } - - void setKeyLightColor(const rgbColor& value) { - _keyLightColor[RED_INDEX] = value[RED_INDEX]; - _keyLightColor[GREEN_INDEX] = value[GREEN_INDEX]; - _keyLightColor[BLUE_INDEX] = value[BLUE_INDEX]; - } - - glm::vec3 getKeyLightColorVec3() const { - const quint8 MAX_COLOR = 255; - glm::vec3 color = { (float)_keyLightColor[RED_INDEX] / (float)MAX_COLOR, - (float)_keyLightColor[GREEN_INDEX] / (float)MAX_COLOR, - (float)_keyLightColor[BLUE_INDEX] / (float)MAX_COLOR }; - return color; - } - - - float getKeyLightIntensity() const { return _keyLightIntensity; } - void setKeyLightIntensity(float value) { _keyLightIntensity = value; } - - float getKeyLightAmbientIntensity() const { return _keyLightAmbientIntensity; } - void setKeyLightAmbientIntensity(float value) { _keyLightAmbientIntensity = value; } - - const glm::vec3& getKeyLightDirection() const { return _keyLightDirection; } - void setKeyLightDirection(const glm::vec3& value) { _keyLightDirection = value; } - */ - protected: - // void setFromOldAnimationSettings(const QString& value); }; From f38303a9a5890c6fa75a73d776089dabd4b2d27e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 10:36:31 -0700 Subject: [PATCH 0397/1003] 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 0398/1003] 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 0399/1003] 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 ff57b73bd6293697c77198c8cc4531a0a4d5e1ef Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 10:53:21 -0700 Subject: [PATCH 0400/1003] Creating the COlorUTils in shared to deal with all the standard color conversions --- .../src/EntityTreeRenderer.cpp | 4 +-- libraries/shared/src/ColorUtils.h | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 libraries/shared/src/ColorUtils.h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ee247b5b62..68719cd265 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -270,8 +271,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetStageYearTime(); _hasPreviousZone = true; } - auto xcolor = zone->getKeyLightProperties().getColor(); - scene->setKeyLightColor(glm::vec3(xcolor.red / 256.0f, xcolor.green / 256.0f, xcolor.blue / 256.0f)); + scene->setKeyLightColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor())); scene->setKeyLightIntensity(zone->getKeyLightProperties().getIntensity()); scene->setKeyLightAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity()); scene->setKeyLightDirection(zone->getKeyLightProperties().getDirection()); diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h new file mode 100644 index 0000000000..f9133a474a --- /dev/null +++ b/libraries/shared/src/ColorUtils.h @@ -0,0 +1,30 @@ +// +// ColorUtils.h +// libraries/shared/src +// +// Created by Sam Gateau on 10/24/15. +// 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_ColorUtils_h +#define hifi_ColorUtils_h + +#include +#include + +#include "DependencyManager.h" + +class ColorUtils { +public: + inline static glm::vec3 toVec3(const xColor& color); +}; + +inline glm::vec3 ColorUtils::toVec3(const xColor& color) { + const float ONE_OVER_256 = 1.0f / 256.0f; + return glm::vec3(color.red * ONE_OVER_256, color.green * ONE_OVER_256, color.blue * ONE_OVER_256); +} + +#endif // hifi_ColorUtils_h \ No newline at end of file From 691e5f3be0880f54872e4143f0e2dc05891cb5d7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 11:07:54 -0700 Subject: [PATCH 0401/1003] 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 0402/1003] 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 0403/1003] 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 9bead94faee6fc225e23959dd7af3876e22c14a0 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 12:07:12 -0700 Subject: [PATCH 0404/1003] Remove cmake debug code --- tests/render-utils/CMakeLists.txt | 2 -- tests/shaders/CMakeLists.txt | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index 62ed401d2e..b56297d05e 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -8,6 +8,4 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries link_hifi_libraries(render-utils gpu shared) -message(${PROJECT_BINARY_DIR}) - copy_dlls_beside_windows_executable() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index 9b2a079e88..42766c98e3 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -17,6 +17,4 @@ include_directories("${PROJECT_BINARY_DIR}/../../libraries/render-utils/") include_directories("${PROJECT_BINARY_DIR}/../../libraries/entities-renderer/") include_directories("${PROJECT_BINARY_DIR}/../../libraries/model/") -message(${PROJECT_BINARY_DIR}) - copy_dlls_beside_windows_executable() From 8e2c269cc0c66a57f2a1f86acc5c82c3abab2442 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 12:24:20 -0700 Subject: [PATCH 0405/1003] doing the correct conversion for byte to normalize float... --- libraries/shared/src/ColorUtils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index f9133a474a..1ef8420cd7 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -23,8 +23,8 @@ public: }; inline glm::vec3 ColorUtils::toVec3(const xColor& color) { - const float ONE_OVER_256 = 1.0f / 256.0f; - return glm::vec3(color.red * ONE_OVER_256, color.green * ONE_OVER_256, color.blue * ONE_OVER_256); + const float ONE_OVER_255 = 1.0f / 255.0f; + return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); } #endif // hifi_ColorUtils_h \ No newline at end of file From fc8184ffedb3286d102fce49b1370e70e838f3ad Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 12:25:51 -0700 Subject: [PATCH 0406/1003] add arcade sound --- examples/toybox/AC_scripts/toybox_sounds.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/toybox/AC_scripts/toybox_sounds.js b/examples/toybox/AC_scripts/toybox_sounds.js index 67985a5938..10c7135196 100644 --- a/examples/toybox/AC_scripts/toybox_sounds.js +++ b/examples/toybox/AC_scripts/toybox_sounds.js @@ -65,7 +65,7 @@ var soundMap = [{ y: 495.60, z: 502.08 }, - volume: 0.25, + volume: 0.05, loop: true } }, { @@ -73,14 +73,27 @@ var soundMap = [{ url: "http://hifi-public.s3.amazonaws.com/ryan/dogs_barking_1.L.wav", audioOptions: { position: { - x: 551.61, + x: 523, y: 494.88, - z: 502.00 + z: 469 }, - volume: 0.15, + volume: 0.05, loop: false }, playAtInterval: 60 * 1000 +}, { + name: 'arcade game', + url: "http://hifi-public.s3.amazonaws.com/ryan/ARCADE_GAMES_VID.L.L.wav", + audioOptions: { + position: { + x: 543.77, + y: 495.07, + z: 502.25 + }, + volume: 0.01, + loop: false, + }, + playAtInterval: 90 * 1000 }]; function loadSounds() { From d91d0a20271c0ffdc063e37ea20c59b9080c7815 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 26 Oct 2015 12:47:31 -0700 Subject: [PATCH 0407/1003] 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 837430329b2df0aae07d1608933d1fa3e8a34dba Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 13:02:17 -0700 Subject: [PATCH 0408/1003] Fix collision start/end in single physics simulation to be lost --- libraries/physics/src/PhysicsEngine.cpp | 9 ++++++--- libraries/physics/src/ThreadSafeDynamicsWorld.cpp | 3 ++- libraries/physics/src/ThreadSafeDynamicsWorld.h | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 75273e62ba..1e87fc9a5d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -251,7 +251,12 @@ void PhysicsEngine::stepSimulation() { _characterController->preSimulation(timeStep); } - int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); + auto onSubStep = [this]() { + updateContactMap(); + _hasOutgoingChanges = true; + }; + + int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); if (numSubsteps > 0) { BT_PROFILE("postSimulation"); _numSubsteps += (uint32_t)numSubsteps; @@ -260,8 +265,6 @@ void PhysicsEngine::stepSimulation() { if (_characterController) { _characterController->postSimulation(); } - updateContactMap(); - _hasOutgoingChanges = true; } } diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index b59103339c..8bf7cdab20 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -27,7 +27,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { } -int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { +int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) { BT_PROFILE("stepSimulation"); int subSteps = 0; if (maxSubSteps) { @@ -70,6 +70,7 @@ int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, for (int i=0;i + +using SubStepCallback = std::function; + ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld { public: BT_DECLARE_ALIGNED_ALLOCATOR(); @@ -34,7 +38,7 @@ public: btCollisionConfiguration* collisionConfiguration); // virtual overrides from btDiscreteDynamicsWorld - int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); + int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); void synchronizeMotionStates(); // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated From 913026280c082a0d7df6311a1136ca95e45afac1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 13:02:39 -0700 Subject: [PATCH 0409/1003] Fix EntityTreeRenderer dropping contact end events --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index d5bf0bde8a..bed6ae1d0f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -774,7 +774,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons } // Don't respond to small continuous contacts. const float COLLISION_MINUMUM_PENETRATION = 0.002f; - if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) { + if ((collision.type == CONTACT_EVENT_TYPE_CONTINUE) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) { return; } From 990764d85577ed5f9e167b3fab91bbb6f1176407 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 26 Oct 2015 13:29:13 -0700 Subject: [PATCH 0410/1003] 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 8d0aaed41ae09f2ef47e0390b3f4e254303dcf3c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 26 Oct 2015 13:50:21 -0700 Subject: [PATCH 0411/1003] fix bug that was deleting actions with 0 ttl. in js interface, action parameter 'lifetime' is now called 'ttl' --- examples/controllers/handControllerGrab.js | 24 ++++++++--------- examples/grab.js | 8 +++--- interface/src/InterfaceActionFactory.cpp | 3 ++- interface/src/avatar/AvatarActionHold.cpp | 8 +++--- .../entities/src/EntityActionInterface.h | 1 + libraries/physics/src/ObjectAction.cpp | 26 ++++++++++++++----- libraries/physics/src/ObjectAction.h | 9 +++++-- libraries/physics/src/ObjectActionOffset.cpp | 9 ++++--- libraries/physics/src/ObjectActionSpring.cpp | 8 +++--- 9 files changed, 62 insertions(+), 34 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 80fb4c8e40..cb445a0960 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -74,8 +74,8 @@ var MSEC_PER_SEC = 1000.0; // these control how long an abandoned pointer line will hang around var LIFETIME = 10; -var ACTION_LIFETIME = 15; // seconds -var ACTION_LIFETIME_REFRESH = 5; +var ACTION_TTL = 15; // seconds +var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; @@ -422,12 +422,12 @@ function MyController(hand, triggerAction) { targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, tag: getTag(), - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } - this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); if (this.actionID !== null) { this.setState(STATE_CONTINUE_DISTANCE_HOLDING); @@ -524,9 +524,9 @@ function MyController(hand, triggerAction) { linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }); - this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); }; this.nearGrabbing = function() { @@ -579,12 +579,12 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; } else { - this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); this.setState(STATE_CONTINUE_NEAR_GRABBING); if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); @@ -624,16 +624,16 @@ function MyController(hand, triggerAction) { this.currentObjectTime = now; Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); - if (this.actionTimeout - now < ACTION_LIFETIME_REFRESH * MSEC_PER_SEC) { - // if less than a 5 seconds left, refresh the actions lifetime + if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { + // if less than a 5 seconds left, refresh the actions ttl Entities.updateAction(this.grabbedEntity, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }); - this.actionTimeout = now + (ACTION_LIFETIME * MSEC_PER_SEC); + this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); } }; diff --git a/examples/grab.js b/examples/grab.js index 1a02911db9..ee6c3c4de5 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -47,7 +47,7 @@ var IDENTITY_QUAT = { z: 0, w: 0 }; -var ACTION_LIFETIME = 10; // seconds +var ACTION_TTL = 10; // seconds function getTag() { return "grab-" + MyAvatar.sessionUUID; @@ -403,7 +403,7 @@ Grabber.prototype.moveEvent = function(event) { var actionArgs = { tag: getTag(), - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }; if (this.mode === "rotate") { @@ -424,7 +424,7 @@ Grabber.prototype.moveEvent = function(event) { targetRotation: this.lastRotation, angularTimeScale: 0.1, tag: getTag(), - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }; } else { @@ -459,7 +459,7 @@ Grabber.prototype.moveEvent = function(event) { targetPosition: this.targetPosition, linearTimeScale: 0.1, tag: getTag(), - lifetime: ACTION_LIFETIME + ttl: ACTION_TTL }; diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp index f814df9a99..67b3b4a649 100644 --- a/interface/src/InterfaceActionFactory.cpp +++ b/interface/src/InterfaceActionFactory.cpp @@ -66,7 +66,8 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt if (action) { action->deserialize(data); if (action->lifetimeIsOver()) { - qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation"; + qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation --" + << action->getExpires() << "<" << usecTimestampNow(); return nullptr; } } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index d2f5514e10..238e48d2fd 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -243,7 +243,7 @@ QByteArray AvatarActionHold::serialize() const { dataStream << _linearTimeScale; dataStream << _hand; - dataStream << _expires + getEntityServerClockSkew(); + dataStream << localTimeToServerTime(_expires); dataStream << _tag; dataStream << _kinematic; dataStream << _kinematicSetVelocity; @@ -277,8 +277,10 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) { _angularTimeScale = _linearTimeScale; dataStream >> _hand; - dataStream >> _expires; - _expires -= getEntityServerClockSkew(); + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; dataStream >> _kinematic; dataStream >> _kinematicSetVelocity; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index d97b5e8106..b257df3325 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -47,6 +47,7 @@ public: static QString actionTypeToString(EntityActionType actionType); virtual bool lifetimeIsOver() { return false; } + virtual quint64 getExpires() { return 0; } bool locallyAddedButNotYetReceived = false; diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index ce4ebfd22f..b58e37e495 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -85,11 +85,11 @@ bool ObjectAction::updateArguments(QVariantMap arguments) { quint64 previousExpires = _expires; QString previousTag = _tag; - bool lifetimeSet = true; - float lifetime = EntityActionInterface::extractFloatArgument("action", arguments, "lifetime", lifetimeSet, false); - if (lifetimeSet) { + bool ttlSet = true; + float ttl = EntityActionInterface::extractFloatArgument("action", arguments, "ttl", ttlSet, false); + if (ttlSet) { quint64 now = usecTimestampNow(); - _expires = now + (quint64)(lifetime * USECS_PER_SECOND); + _expires = now + (quint64)(ttl * USECS_PER_SECOND); } else { _expires = 0; } @@ -114,10 +114,10 @@ QVariantMap ObjectAction::getArguments() { QVariantMap arguments; withReadLock([&]{ if (_expires == 0) { - arguments["lifetime"] = 0.0f; + arguments["ttl"] = 0.0f; } else { quint64 now = usecTimestampNow(); - arguments["lifetime"] = (float)(_expires - now) / (float)USECS_PER_SECOND; + arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND; } arguments["tag"] = _tag; }); @@ -245,3 +245,17 @@ bool ObjectAction::lifetimeIsOver() { } return false; } + +quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) { + if (timeValue == 0) { + return 0; + } + return timeValue + getEntityServerClockSkew(); +} + +quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) { + if (timeValue == 0) { + return 0; + } + return timeValue - getEntityServerClockSkew(); +} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 98e58475c6..3c8574f6ff 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -47,11 +47,10 @@ public: virtual void deserialize(QByteArray serializedArguments) = 0; virtual bool lifetimeIsOver(); + virtual quint64 getExpires() { return _expires; } protected: - int getEntityServerClockSkew() const; - virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); virtual void setPosition(glm::vec3 position); @@ -68,6 +67,12 @@ protected: quint64 _expires; // in seconds since epoch QString _tag; + + quint64 localTimeToServerTime(quint64 timeValue); + quint64 serverTimeToLocalTime(quint64 timeValue); + +private: + int getEntityServerClockSkew() const; }; #endif // hifi_ObjectAction_h diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index b6edf22ffc..2cfd98497b 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -160,7 +160,7 @@ QByteArray ObjectActionOffset::serialize() const { dataStream << _linearDistance; dataStream << _linearTimeScale; dataStream << _positionalTargetSet; - dataStream << _expires + getEntityServerClockSkew(); + dataStream << localTimeToServerTime(_expires); dataStream << _tag; }); @@ -189,8 +189,11 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) { dataStream >> _linearDistance; dataStream >> _linearTimeScale; dataStream >> _positionalTargetSet; - dataStream >> _expires; - _expires -= getEntityServerClockSkew(); + + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; _active = true; }); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index a74756eb53..c1cd2db5ca 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -198,7 +198,7 @@ QByteArray ObjectActionSpring::serialize() const { dataStream << _rotationalTarget; dataStream << _angularTimeScale; dataStream << _rotationalTargetSet; - dataStream << _expires + getEntityServerClockSkew(); + dataStream << localTimeToServerTime(_expires); dataStream << _tag; }); @@ -232,8 +232,10 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { dataStream >> _angularTimeScale; dataStream >> _rotationalTargetSet; - dataStream >> _expires; - _expires -= getEntityServerClockSkew(); + quint64 serverExpires; + dataStream >> serverExpires; + _expires = serverTimeToLocalTime(serverExpires); + dataStream >> _tag; _active = true; From 0dff037f5601b011c992a0565f0d3c49291b61e4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 26 Oct 2015 13:55:07 -0700 Subject: [PATCH 0412/1003] fuck you, const! --- libraries/physics/src/ObjectAction.cpp | 4 ++-- libraries/physics/src/ObjectAction.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index b58e37e495..8ed06aea5d 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -246,14 +246,14 @@ bool ObjectAction::lifetimeIsOver() { return false; } -quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) { +quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) const { if (timeValue == 0) { return 0; } return timeValue + getEntityServerClockSkew(); } -quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) { +quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) const { if (timeValue == 0) { return 0; } diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 3c8574f6ff..fca446aec4 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -68,8 +68,8 @@ protected: quint64 _expires; // in seconds since epoch QString _tag; - quint64 localTimeToServerTime(quint64 timeValue); - quint64 serverTimeToLocalTime(quint64 timeValue); + quint64 localTimeToServerTime(quint64 timeValue) const; + quint64 serverTimeToLocalTime(quint64 timeValue) const; private: int getEntityServerClockSkew() const; From f5d12ee2c60a139cbde514d3a398cdbe098f60f3 Mon Sep 17 00:00:00 2001 From: black plastick Date: Mon, 26 Oct 2015 17:46:08 -0400 Subject: [PATCH 0413/1003] Added AvatarList script events for assigment client scripts: AvatarList.avatarAddedEvent(sessionUUID); AvatarList.avatarRemovedEvent(sessionUUID); --- assignment-client/src/avatars/AvatarMixer.cpp | 4 ++-- libraries/avatars/src/AvatarHashMap.cpp | 4 +++- libraries/avatars/src/AvatarHashMap.h | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 833b53b729..ae6a8247c1 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -254,10 +254,10 @@ void AvatarMixer::broadcastAvatarData() { // potentially update the max full rate distance for this frame maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - if (distanceToAvatar != 0.0f + if (distanceToAvatar != 0.0f && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { return; - } + } AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 520bb34887..c02fe98b19 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -44,7 +44,7 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe avatar->setSessionUUID(sessionUUID); avatar->setOwningAvatarMixer(mixerWeakPointer); _avatarHash.insert(sessionUUID, avatar); - + emit avatarAddedEvent(sessionUUID); return avatar; } @@ -131,10 +131,12 @@ void AvatarHashMap::processKillAvatar(QSharedPointer packet, SharedNod // read the node id QUuid sessionUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); removeAvatar(sessionUUID); + } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID) { _avatarHash.remove(sessionUUID); + emit avatarRemovedEvent(sessionUUID); } void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 804233b76a..f90c61ec4d 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -33,6 +33,10 @@ public: const AvatarHash& getAvatarHash() { return _avatarHash; } int size() { return _avatarHash.size(); } +signals: + void avatarAddedEvent(const QUuid& sessionUUID); + void avatarRemovedEvent(const QUuid& sessionUUID); + public slots: bool isAvatarInRange(const glm::vec3 & position, const float range); From f436857a6241e2f4874fa615efe310aa8508f291 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 15:28:35 -0700 Subject: [PATCH 0414/1003] add reset buttons for basketballs and targets --- unpublishedScripts/basketballsResetter.js | 111 ++++++++++++++++++ unpublishedScripts/hiddenEntityReset.js | 109 +++++++++++++++++- unpublishedScripts/immediateClientReset.js | 7 +- unpublishedScripts/masterReset.js | 128 +++++++++++++++++++-- unpublishedScripts/targetsResetter.js | 128 +++++++++++++++++++++ 5 files changed, 461 insertions(+), 22 deletions(-) create mode 100644 unpublishedScripts/basketballsResetter.js create mode 100644 unpublishedScripts/targetsResetter.js diff --git a/unpublishedScripts/basketballsResetter.js b/unpublishedScripts/basketballsResetter.js new file mode 100644 index 0000000000..8574bc6e92 --- /dev/null +++ b/unpublishedScripts/basketballsResetter.js @@ -0,0 +1,111 @@ +// +// +// Created by James B. Pollack @imgntn on 10/26/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 +var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +(function() { + + var _this; + Resetter = function() { + _this = this; + }; + + Resetter.prototype = { + + startFarGrabNonColliding: function() { + this.resetObjects(); + }, + + clickReleaseOnEntity: function() { + this.resetObjects(); + }, + + resetObjects: function() { + var ids = Entities.findEntities(this.initialProperties.position, 75); + var i; + for (i = 0; i < ids.length; i++) { + var id = ids[i]; + var properties = Entities.getEntityProperties(id, "name"); + if (properties.name === "Hifi-Basketball") { + Entities.deleteEntity(id); + } + } + + this.createBasketballs(); + }, + + createBasketballs: function() { + var NUMBER_OF_BALLS = 4; + var DIAMETER = 0.30; + var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; + var basketballCollisionSoundURL = HIFI_PUBLIC_BUCKET + "sounds/basketball/basketball.wav"; + + var position = { + x: 542.86, + y: 494.84, + z: 475.06 + }; + var collidingBalls = []; + + var i; + for (i = 0; i < NUMBER_OF_BALLS; i++) { + var ballPosition = { + x: position.x, + y: position.y + DIAMETER * 2, + z: position.z + (DIAMETER) - (DIAMETER * i) + }; + var newPosition = { + x: position.x + (DIAMETER * 2) - (DIAMETER * i), + y: position.y + DIAMETER * 2, + z: position.z + }; + var collidingBall = Entities.addEntity({ + type: "Model", + name: 'Hifi-Basketball', + shapeType: 'Sphere', + position: newPosition, + dimensions: { + x: DIAMETER, + y: DIAMETER, + z: DIAMETER + }, + restitution: 1.0, + linearDamping: 0.00001, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + collisionsSoundURL: basketballCollisionSoundURL, + ignoreForCollisions: false, + modelURL: basketballURL, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + invertSolidWhileHeld: true + } + }) + }); + + collidingBalls.push(collidingBall); + + } + }, + + preload: function(entityID) { + this.initialProperties = Entities.getEntityProperties(entityID); + this.entityID = entityID; + }, + + }; + + return new Resetter(); +}); \ No newline at end of file diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index e441db3aa6..d81c1a125f 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -22,7 +22,8 @@ var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); - + var basketballResetterScriptURL = Script.resolvePath('basketballsResetter.js'); + var targetsResetterScriptURL = Script.resolvePath('targetsResetter.js'); ResetSwitch = function() { _this = this; @@ -110,9 +111,12 @@ }); createPingPongBallGun(); + createTargets(); + createTargetResetter(); createBasketballHoop(); createBasketballRack(); + createBasketballResetter(); createGates(); @@ -120,8 +124,6 @@ // Handles toggling of all sconce lights createLights(); - - createCat({ x: 551.09, y: 494.98, @@ -135,7 +137,6 @@ z: 503.91 }); - createTargets(); } @@ -275,10 +276,11 @@ }) }); - var collidingBalls = []; + function createCollidingBalls() { var position = rackStartPosition; + var collidingBalls = []; var i; for (i = 0; i < NUMBER_OF_BALLS; i++) { @@ -334,6 +336,103 @@ } + function createBasketballResetter() { + + var position = { + x: 542.86, + y: 494.44, + z: 475.06 + }; + + var dimensions = { + x: 0.5, + y: 0.1, + z: 0.01 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + + var resetter = Entities.addEntity({ + type: "Text", + position: position, + name: "Basketball Resetter", + script: basketballResetterScriptURL, + rotation: rotation, + dimensions: dimensions, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + text: "RESET BALLS", + lineHeight: 0.07, + faceCamera: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + wantsTrigger: true + } + }) + }); + + + } + + function createTargetResetter() { + var dimensions = { + x: 0.5, + y: 0.1, + z: 0.01 + }; + + var position = { + x: 548.68, + y: 495.30, + z: 509.74 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + + var resetter = Entities.addEntity({ + type: "Text", + position: position, + name: "Target Resetter", + script: targetsResetterScriptURL, + rotation: rotation, + dimensions: dimensions, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + faceCamera: true, + text: "RESET TARGETS", + lineHeight: 0.07, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + wantsTrigger: true + } + }) + + }); + } + + function createTargets() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; diff --git a/unpublishedScripts/immediateClientReset.js b/unpublishedScripts/immediateClientReset.js index 0a2e9383a2..2088160727 100644 --- a/unpublishedScripts/immediateClientReset.js +++ b/unpublishedScripts/immediateClientReset.js @@ -8,15 +8,11 @@ /*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ - var masterResetScript = Script.resolvePath("masterReset.js"); var hiddenEntityScriptURL = Script.resolvePath("hiddenEntityReset.js"); - Script.include(masterResetScript); - - function createHiddenMasterSwitch() { var resetKey = "resetMe"; @@ -31,7 +27,6 @@ function createHiddenMasterSwitch() { }); } - var entities = Entities.findEntities(MyAvatar.position, 100); entities.forEach(function(entity) { @@ -41,5 +36,7 @@ entities.forEach(function(entity) { Entities.deleteEntity(entity); } }); + createHiddenMasterSwitch(); + MasterReset(); \ No newline at end of file diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 956db41235..099c903ea3 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -14,16 +14,16 @@ var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js"); Script.include(utilitiesScript); - var sprayPaintScriptURL = Script.resolvePath("../examples/toybox/spray_paint/sprayPaintCan.js"); - var catScriptURL = Script.resolvePath("../examples/toybox/cat/cat.js"); - var flashlightScriptURL = Script.resolvePath('../examples/toybox/flashlight/flashlight.js'); - var pingPongScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/pingPongGun.js'); - var wandScriptURL = Script.resolvePath("../examples/toybox/bubblewand/wand.js"); - var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); - var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); - var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); - - +var sprayPaintScriptURL = Script.resolvePath("../examples/toybox/spray_paint/sprayPaintCan.js"); +var catScriptURL = Script.resolvePath("../examples/toybox/cat/cat.js"); +var flashlightScriptURL = Script.resolvePath('../examples/toybox/flashlight/flashlight.js'); +var pingPongScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/pingPongGun.js'); +var wandScriptURL = Script.resolvePath("../examples/toybox/bubblewand/wand.js"); +var dollScriptURL = Script.resolvePath("../examples/toybox/doll/doll.js"); +var lightsScriptURL = Script.resolvePath("../examples/toybox/lights/lightSwitch.js"); +var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); +var basketballResetterScriptURL = Script.resolvePath('basketballsResetter.js'); +var targetsResetterScriptURL = Script.resolvePath('targetsResetter.js'); MasterReset = function() { var resetKey = "resetMe"; @@ -84,9 +84,12 @@ MasterReset = function() { }); createPingPongBallGun(); + createTargets(); + createTargetResetter(); createBasketballHoop(); createBasketballRack(); + createBasketballResetter(); createGates(); @@ -109,7 +112,7 @@ MasterReset = function() { z: 503.91 }); - createTargets(); + } @@ -201,6 +204,7 @@ MasterReset = function() { }); } + function createBasketballRack() { var NUMBER_OF_BALLS = 4; var DIAMETER = 0.30; @@ -249,10 +253,11 @@ MasterReset = function() { }) }); - var collidingBalls = []; + function createCollidingBalls() { var position = rackStartPosition; + var collidingBalls = []; var i; for (i = 0; i < NUMBER_OF_BALLS; i++) { @@ -308,6 +313,105 @@ MasterReset = function() { } + + function createBasketballResetter() { + + var position = { + x: 542.86, + y: 494.44, + z: 475.06 + }; + + var dimensions = { + x: 0.5, + y: 0.1, + z: 0.01 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + + var resetter = Entities.addEntity({ + type: "Text", + position: position, + name: "Basketball Resetter", + script: basketballResetterScriptURL, + rotation: rotation, + dimensions: dimensions, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + text: "RESET BALLS", + lineHeight: 0.07, + faceCamera: true, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + wantsTrigger: true + } + }) + }); + + + } + + function createTargetResetter() { + var dimensions = { + x: 0.5, + y: 0.1, + z: 0.01 + }; + + var position = { + x: 548.68, + y: 495.30, + z: 509.74 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); + + var resetter = Entities.addEntity({ + type: "Text", + position: position, + name: "Target Resetter", + script: targetsResetterScriptURL, + rotation: rotation, + dimensions: dimensions, + backgroundColor: { + red: 0, + green: 0, + blue: 0 + }, + textColor: { + red: 255, + green: 255, + blue: 255 + }, + faceCamera: true, + text: "RESET TARGETS", + lineHeight: 0.07, + userData: JSON.stringify({ + resetMe: { + resetMe: true + }, + grabbableKey: { + wantsTrigger: true + } + }) + + }); + } + + + function createTargets() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; diff --git a/unpublishedScripts/targetsResetter.js b/unpublishedScripts/targetsResetter.js new file mode 100644 index 0000000000..716e98e1e1 --- /dev/null +++ b/unpublishedScripts/targetsResetter.js @@ -0,0 +1,128 @@ +// +// +// Created by James B. Pollack @imgntn on 10/26/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() { + var targetsScriptURL = Script.resolvePath('../examples/toybox/ping_pong_gun/wallTarget.js'); + + var _this; + Resetter = function() { + _this = this; + }; + + Resetter.prototype = { + + startFarGrabNonColliding: function() { + this.resetObjects(); + }, + + clickReleaseOnEntity: function() { + this.resetObjects(); + }, + + resetObjects: function() { + var ids = Entities.findEntities(this.initialProperties.position, 50); + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; + var properties = Entities.getEntityProperties(id, "name"); + if (properties.name === "Hifi-Target") { + Entities.deleteEntity(id); + } + } + this.createTargets(); + }, + + preload: function(entityID) { + this.initialProperties = Entities.getEntityProperties(entityID); + this.entityID = entityID; + }, + + createTargets: function() { + + var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target_collision_hull.obj'; + + var MINIMUM_MOVE_LENGTH = 0.05; + var RESET_DISTANCE = 0.5; + var TARGET_USER_DATA_KEY = 'hifi-ping_pong_target'; + var NUMBER_OF_TARGETS = 6; + var TARGETS_PER_ROW = 3; + + var TARGET_DIMENSIONS = { + x: 0.06, + y: 0.42, + z: 0.42 + }; + + var VERTICAL_SPACING = TARGET_DIMENSIONS.y + 0.5; + var HORIZONTAL_SPACING = TARGET_DIMENSIONS.z + 0.5; + + + var startPosition = { + x: 548.68, + y: 497.30, + z: 509.74 + }; + + var rotation = Quat.fromPitchYawRollDegrees(0, -55.25, 0); + + var targets = []; + + function addTargets() { + var i; + var row = -1; + for (i = 0; i < NUMBER_OF_TARGETS; i++) { + + if (i % TARGETS_PER_ROW === 0) { + row++; + } + + var vHat = Quat.getFront(rotation); + var spacer = HORIZONTAL_SPACING * (i % TARGETS_PER_ROW) + (row * HORIZONTAL_SPACING / 2); + var multiplier = Vec3.multiply(spacer, vHat); + var position = Vec3.sum(startPosition, multiplier); + position.y = startPosition.y - (row * VERTICAL_SPACING); + + var targetProperties = { + name: 'Hifi-Target', + type: 'Model', + modelURL: MODEL_URL, + shapeType: 'compound', + collisionsWillMove: true, + dimensions: TARGET_DIMENSIONS, + compoundShapeURL: COLLISION_HULL_URL, + position: position, + rotation: rotation, + script: targetsScriptURL, + userData: JSON.stringify({ + originalPositionKey: { + originalPosition: position + }, + resetMe: { + resetMe: true + }, + grabbableKey: { + grabbable: false + } + }) + }; + + var target = Entities.addEntity(targetProperties); + targets.push(target); + + } + } + + addTargets(); + + } + + }; + + return new Resetter(); +}); \ No newline at end of file From 90131f129129129cfb05b27c5aae58304f7a62b3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 16:00:14 -0700 Subject: [PATCH 0415/1003] make target reset wider --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index d81c1a125f..574504fd9f 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -387,7 +387,7 @@ function createTargetResetter() { var dimensions = { - x: 0.5, + x: 1, y: 0.1, z: 0.01 }; diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 099c903ea3..b90fbeb703 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -365,7 +365,7 @@ MasterReset = function() { function createTargetResetter() { var dimensions = { - x: 0.5, + x: 1, y: 0.1, z: 0.01 }; @@ -377,7 +377,7 @@ MasterReset = function() { }; var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - + var resetter = Entities.addEntity({ type: "Text", position: position, From da5db326e829e59c866ad514fc65a80a3975402a Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 16:03:10 -0700 Subject: [PATCH 0416/1003] make cat quieter --- examples/toybox/AC_scripts/toybox_sounds.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toybox/AC_scripts/toybox_sounds.js b/examples/toybox/AC_scripts/toybox_sounds.js index 10c7135196..ee87943f00 100644 --- a/examples/toybox/AC_scripts/toybox_sounds.js +++ b/examples/toybox/AC_scripts/toybox_sounds.js @@ -65,7 +65,7 @@ var soundMap = [{ y: 495.60, z: 502.08 }, - volume: 0.05, + volume: 0.03, loop: true } }, { From 88a312f992ffa39eb71b69a88fb69fc4d7f0869c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 16:28:18 -0700 Subject: [PATCH 0417/1003] add functional js library... mmm, curry --- examples/libraries/fjs.js | 313 +++++++++++++++++++++++++++++++ examples/libraries/fjsExample.js | 9 + 2 files changed, 322 insertions(+) create mode 100644 examples/libraries/fjs.js create mode 100644 examples/libraries/fjsExample.js diff --git a/examples/libraries/fjs.js b/examples/libraries/fjs.js new file mode 100644 index 0000000000..738e9ae60f --- /dev/null +++ b/examples/libraries/fjs.js @@ -0,0 +1,313 @@ + loadFJS = function(){ + return fjs(); +} + +var fjs = function() { + "use strict"; + + var fjs = {}, + hardReturn = "hardReturn;"; + + var lambda = function(exp) { + if (!fjs.isString(exp)) { + return; + } + + var parts = exp.match(/(.*)\s*[=-]>\s*(.*)/); + parts.shift(); + + var params = parts.shift() + .replace(/^\s*|\s(?=\s)|\s*$|,/g, "").split(" "); + var body = parts.shift(); + + parts = ((!/\s*return\s+/.test(body)) ? "return " : "") + body; + params.push(parts); + + return Function.apply({}, params); + }; + + var sliceArgs = function(args) { + return args.length > 0 ? [].slice.call(args, 0) : []; + }; + + fjs.isFunction = function(obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }; + + fjs.isObject = function(obj) { + return fjs.isFunction(obj) || (!!obj && typeof(obj) === "object"); + }; + + fjs.isArray = function(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + }; + + var checkFunction = function(func) { + if (!fjs.isFunction(func)) { + func = lambda(func); + if (!fjs.isFunction(func)) { + throw "fjs Error: Invalid function"; + } + } + return func; + }; + + fjs.curry = function(func) { + func = checkFunction(func); + return function inner() { + var _args = sliceArgs(arguments); + if (_args.length === func.length) { + return func.apply(null, _args); + } else if (_args.length > func.length) { + var initial = func.apply(null, _args); + return fjs.fold(func, initial, _args.slice(func.length)); + } else { + return function() { + var args = sliceArgs(arguments); + return inner.apply(null, _args.concat(args)); + }; + } + }; + }; + + fjs.each = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + if (!fjs.exists(items) || !fjs.isArray(items)) { + return; + } + for (var i = 0; i < items.length; i += 1) { + if (iterator.call(null, items[i], i) === hardReturn) { + return; + } + } + }); + + fjs.map = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var mapped = []; + fjs.each(function() { + mapped.push(iterator.apply(null, arguments)); + }, items); + return mapped; + }); + + fjs.fold = fjs.foldl = fjs.curry(function(iterator, cumulate, items) { + iterator = checkFunction(iterator); + fjs.each(function(item, i) { + cumulate = iterator.call(null, cumulate, item, i); + }, items); + return cumulate; + }); + + fjs.reduce = fjs.reducel = fjs.foldll = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var cumulate = items[0]; + items.shift(); + return fjs.fold(iterator, cumulate, items); + }); + + fjs.clone = function(items) { + var clone = []; + fjs.each(function(item) { + clone.push(item); + }, items); + return clone; + }; + + fjs.first = fjs.head = fjs.take = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var first; + fjs.each(function(item) { + if (iterator.call(null, item)) { + first = item; + return hardReturn; + } + }, items); + return first; + }); + + fjs.rest = fjs.tail = fjs.drop = fjs.curry(function(iterator, items) { + var result = fjs.select(iterator, items); + result.shift(); + return result; + }); + + fjs.last = fjs.curry(function(iterator, items) { + var itemsClone = fjs.clone(items); + return fjs.first(iterator, itemsClone.reverse()); + }); + + fjs.every = fjs.all = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var isEvery = true; + fjs.each(function(item) { + if (!iterator.call(null, item)) { + isEvery = false; + return hardReturn; + } + }, items); + return isEvery; + }); + + fjs.any = fjs.contains = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var isAny = false; + fjs.each(function(item) { + if (iterator.call(null, item)) { + isAny = true; + return hardReturn; + } + }, items); + return isAny; + }); + + fjs.select = fjs.filter = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var filtered = []; + fjs.each(function(item) { + if (iterator.call(null, item)) { + filtered.push(item); + } + }, items); + return filtered; + }); + + fjs.best = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var compare = function(arg1, arg2) { + return iterator.call(this, arg1, arg2) ? + arg1 : arg2; + }; + return fjs.reduce(compare, items); + }); + + fjs._while = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var result = []; + fjs.each(function(item) { + if (iterator.call(null, item)) { + result.push(item); + } else { + return hardReturn; + } + }, items); + return result; + }); + + fjs.compose = function(funcs) { + var anyInvalid = fjs.any(function(func) { + return !fjs.isFunction(func); + }); + funcs = sliceArgs(arguments).reverse(); + if (anyInvalid(funcs)) { + throw "fjs Error: Invalid function to compose"; + } + return function() { + var args = arguments; + var applyEach = fjs.each(function(func) { + args = [func.apply(null, args)]; + }); + applyEach(funcs); + return args[0]; + }; + }; + + fjs.partition = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var truthy = [], + falsy = []; + fjs.each(function(item) { + (iterator.call(null, item) ? truthy : falsy).push(item); + }, items); + return [truthy, falsy]; + }); + + fjs.group = fjs.curry(function(iterator, items) { + iterator = checkFunction(iterator); + var result = {}; + var group; + fjs.each(function(item) { + group = iterator.call(null, item); + result[group] = result[group] || []; + result[group].push(item); + }, items); + return result; + }); + + fjs.shuffle = function(items) { + var j, t; + fjs.each(function(item, i) { + j = Math.floor(Math.random() * (i + 1)); + t = items[i]; + items[i] = items[j]; + items[j] = t; + }, items); + return items; + }; + + fjs.toArray = function(obj) { + return fjs.map(function(key) { + return [key, obj[key]]; + }, Object.keys(obj)); + }; + + fjs.apply = fjs.curry(function(func, items) { + var args = []; + if (fjs.isArray(func)) { + args = [].slice.call(func, 1); + func = func[0]; + } + return fjs.map(function(item) { + return item[func].apply(item, args); + }, items); + }); + + fjs.assign = fjs.extend = fjs.curry(function(obj1, obj2) { + fjs.each(function(key) { + obj2[key] = obj1[key]; + }, Object.keys(obj1)); + return obj2; + }); + + fjs.prop = function(prop) { + return function(obj) { + return obj[prop]; + }; + }; + + fjs.pluck = fjs.curry(function(prop, items) { + return fjs.map(fjs.prop(prop), items); + }); + + fjs.nub = fjs.unique = fjs.distinct = fjs.curry(function(comparator, items) { + var unique = items.length > 0 ? [items[0]] : []; + + fjs.each(function(item) { + if (!fjs.any(fjs.curry(comparator)(item), unique)) { + unique[unique.length] = item; + } + }, items); + + return unique; + }); + + fjs.exists = function(obj) { + return obj != null; // jshint ignore:line + }; + + fjs.truthy = function(obj) { + return fjs.exists(obj) && obj !== false; + }; + + fjs.falsy = function(obj) { + return !fjs.truthy(obj); + }; + + fjs.each(function(type) { + fjs["is" + type] = function(obj) { + return Object.prototype.toString.call(obj) === "[object " + type + "]"; + }; + }, ["Arguments", "Date", "Number", "RegExp", "String"]); + + return fjs; +} \ No newline at end of file diff --git a/examples/libraries/fjsExample.js b/examples/libraries/fjsExample.js new file mode 100644 index 0000000000..486090a352 --- /dev/null +++ b/examples/libraries/fjsExample.js @@ -0,0 +1,9 @@ +Script.include('fjs.js'); +var fjs = loadFJS(); + +var concatenate = fjs.curry(function(word1, word2) { + return word1 + " " + word2; +}); +var concatenateHello = concatenate("Hello"); +var hi = concatenateHello("World"); +print('anyone listenig?' + hi) \ No newline at end of file From a1cd4a31cbb2786684f7de47b7a65f16c7686606 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 16:41:03 -0700 Subject: [PATCH 0418/1003] fix typo --- examples/libraries/fjsExample.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/fjsExample.js b/examples/libraries/fjsExample.js index 486090a352..78960826fd 100644 --- a/examples/libraries/fjsExample.js +++ b/examples/libraries/fjsExample.js @@ -6,4 +6,4 @@ var concatenate = fjs.curry(function(word1, word2) { }); var concatenateHello = concatenate("Hello"); var hi = concatenateHello("World"); -print('anyone listenig?' + hi) \ No newline at end of file +print('anyone listening? ' + hi) \ No newline at end of file From 1a1ab29978e04302fb49aff32a2afcfd3611b147 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 26 Oct 2015 16:45:43 -0700 Subject: [PATCH 0419/1003] 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 0420/1003] 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 d78fe089f64f7682e13c87cc8fc794b5b846196b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 17:00:54 -0700 Subject: [PATCH 0421/1003] add mit license --- examples/libraries/fjs.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/examples/libraries/fjs.js b/examples/libraries/fjs.js index 738e9ae60f..f2e6c48f59 100644 --- a/examples/libraries/fjs.js +++ b/examples/libraries/fjs.js @@ -1,3 +1,31 @@ +/* + +http://functionaljs.com/ + +https://github.com/leecrossley/functional-js/ + +The MIT License (MIT) +Copyright © 2015 Lee Crossley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + loadFJS = function(){ return fjs(); } From 485e36d824c5d843beec47cde01c525ba272ddca Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 26 Oct 2015 17:11:46 -0700 Subject: [PATCH 0422/1003] Bug fix for hand IK when using the Owen avatar. The IK was assiming that the "Hips" bone index was always 0. This was not the case for Owen. Now we lookup the Hips index and cache it for use during the hipsOffset computation. --- libraries/animation/src/AnimInverseKinematics.cpp | 4 +++- libraries/animation/src/AnimInverseKinematics.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 516d4116d8..42e9472819 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -366,7 +366,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars if (offsetLength > MIN_HIPS_OFFSET_LENGTH) { // but only if offset is long enough float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength); - _relativePoses[0].trans = underPoses[0].trans + scaleFactor * _hipsOffset; + _relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset; } solveWithCyclicCoordinateDescent(targets); @@ -758,8 +758,10 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele if (skeleton) { initConstraints(); _headIndex = _skeleton->nameToJointIndex("Head"); + _hipsIndex = _skeleton->nameToJointIndex("Hips"); } else { clearConstraints(); _headIndex = -1; + _hipsIndex = -1; } } diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index b96dd3711e..f8f7fd9e9e 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -80,6 +80,7 @@ protected: // experimental data for moving hips during IK int _headIndex = -1; + int _hipsIndex = -1; glm::vec3 _hipsOffset = Vectors::ZERO; // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses From 2d873012a34dbeaf6ed546034508ecc1488a817d Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 17:51:55 -0700 Subject: [PATCH 0423/1003] make reset buttons invisible entities instead of text --- unpublishedScripts/hiddenEntityReset.js | 61 +++++++------------------ unpublishedScripts/masterReset.js | 60 ++++++------------------ 2 files changed, 31 insertions(+), 90 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 574504fd9f..ff2f2fa0cc 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -339,39 +339,24 @@ function createBasketballResetter() { var position = { - x: 542.86, - y: 494.44, - z: 475.06 + x: 543.58, + y: 495.47, + z: 469.59 }; var dimensions = { - x: 0.5, - y: 0.1, - z: 0.01 + x: 1.65, + y: 1.71, + z: 1.75 }; - var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - var resetter = Entities.addEntity({ type: "Text", position: position, name: "Basketball Resetter", script: basketballResetterScriptURL, - rotation: rotation, dimensions: dimensions, - backgroundColor: { - red: 0, - green: 0, - blue: 0 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - text: "RESET BALLS", - lineHeight: 0.07, - faceCamera: true, + visible: false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -387,39 +372,24 @@ function createTargetResetter() { var dimensions = { - x: 1, - y: 0.1, - z: 0.01 + x: 0.21, + y: 0.61, + z: 0.21 }; var position = { - x: 548.68, - y: 495.30, - z: 509.74 + x: 548.42, + y: 496.40, + z: 509.61 }; - var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - var resetter = Entities.addEntity({ - type: "Text", + type: "Box", position: position, name: "Target Resetter", script: targetsResetterScriptURL, - rotation: rotation, dimensions: dimensions, - backgroundColor: { - red: 0, - green: 0, - blue: 0 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - faceCamera: true, - text: "RESET TARGETS", - lineHeight: 0.07, + visible: false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -433,6 +403,7 @@ } + function createTargets() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/target.fbx'; diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index b90fbeb703..2b4f978cf9 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -317,39 +317,24 @@ MasterReset = function() { function createBasketballResetter() { var position = { - x: 542.86, - y: 494.44, - z: 475.06 + x: 543.58, + y: 495.47, + z: 469.59 }; var dimensions = { - x: 0.5, - y: 0.1, - z: 0.01 + x: 1.65, + y: 1.71, + z: 1.75 }; - var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - var resetter = Entities.addEntity({ type: "Text", position: position, name: "Basketball Resetter", script: basketballResetterScriptURL, - rotation: rotation, dimensions: dimensions, - backgroundColor: { - red: 0, - green: 0, - blue: 0 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - text: "RESET BALLS", - lineHeight: 0.07, - faceCamera: true, + visible:false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -365,39 +350,24 @@ MasterReset = function() { function createTargetResetter() { var dimensions = { - x: 1, - y: 0.1, - z: 0.01 + x: 0.21, + y: 0.61, + z: 0.21 }; var position = { - x: 548.68, - y: 495.30, - z: 509.74 + x: 548.42, + y: 496.40, + z: 509.61 }; - var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); - var resetter = Entities.addEntity({ - type: "Text", + type: "Box", position: position, name: "Target Resetter", script: targetsResetterScriptURL, - rotation: rotation, dimensions: dimensions, - backgroundColor: { - red: 0, - green: 0, - blue: 0 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - faceCamera: true, - text: "RESET TARGETS", - lineHeight: 0.07, + visible:false, userData: JSON.stringify({ resetMe: { resetMe: true From 7a7f649bf7132c0f7adb66be02a0652dd1738e06 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 17:55:13 -0700 Subject: [PATCH 0424/1003] change from far grab to near grab --- unpublishedScripts/basketballsResetter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpublishedScripts/basketballsResetter.js b/unpublishedScripts/basketballsResetter.js index 8574bc6e92..6335312d57 100644 --- a/unpublishedScripts/basketballsResetter.js +++ b/unpublishedScripts/basketballsResetter.js @@ -17,7 +17,7 @@ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Resetter.prototype = { - startFarGrabNonColliding: function() { + startNearGrabNonColliding: function() { this.resetObjects(); }, From c8e1aca4b672eb7136ca62d378439d386595acb3 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 26 Oct 2015 18:10:45 -0700 Subject: [PATCH 0425/1003] fix box dimensions --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- unpublishedScripts/targetsResetter.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index ff2f2fa0cc..43d23e4b3c 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -351,7 +351,7 @@ }; var resetter = Entities.addEntity({ - type: "Text", + type: "Box", position: position, name: "Basketball Resetter", script: basketballResetterScriptURL, diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 2b4f978cf9..74eb3c85ac 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -329,7 +329,7 @@ MasterReset = function() { }; var resetter = Entities.addEntity({ - type: "Text", + type: "Box", position: position, name: "Basketball Resetter", script: basketballResetterScriptURL, diff --git a/unpublishedScripts/targetsResetter.js b/unpublishedScripts/targetsResetter.js index 716e98e1e1..a522c19593 100644 --- a/unpublishedScripts/targetsResetter.js +++ b/unpublishedScripts/targetsResetter.js @@ -17,7 +17,7 @@ Resetter.prototype = { - startFarGrabNonColliding: function() { + startNearGrabNonColliding: function() { this.resetObjects(); }, From 5bce95189b5a0716185d8a134f00ad468e7b66dc Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Mon, 26 Oct 2015 18:14:57 -0700 Subject: [PATCH 0426/1003] 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 cbade6a095079e1ac8527342c8a5cb0e9fd5b266 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 26 Oct 2015 19:27:22 -0700 Subject: [PATCH 0427/1003] Do not shut down hydra when it says there are no devices. Count to allowedHydraFailures in settings. --- .../input-plugins/src/input-plugins/SixenseManager.cpp | 8 +++++++- .../input-plugins/src/input-plugins/SixenseManager.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3950fdea43..8ea05fc300 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -167,7 +167,10 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { if (sixenseGetNumActiveControllers() == 0) { if (_hydrasConnected) { - qCDebug(inputplugins, "hydra disconnected"); + qCDebug(inputplugins) << "hydra disconnected" << _badDataCount; + if (_badDataCount++ < _allowedBadDataCount) { // gotta get some no-active in a row before we shut things down + return; + } } _hydrasConnected = false; if (_deviceID != 0) { @@ -181,6 +184,7 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { PerformanceTimer perfTimer("sixense"); if (!_hydrasConnected) { _hydrasConnected = true; + _badDataCount = 0; registerToUserInputMapper(*userInputMapper); assignDefaultInputMapping(*userInputMapper); UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); @@ -555,6 +559,7 @@ 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(); } @@ -567,6 +572,7 @@ 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 9fa7f84a86..90bd830650 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -114,6 +114,8 @@ private: #endif bool _hydrasConnected; + int _badDataCount; + int _allowedBadDataCount; static const QString NAME; static const QString HYDRA_ID_STRING; From d34fc35544fa38715d1990f0e09f3201c1f68679 Mon Sep 17 00:00:00 2001 From: black plastick Date: Tue, 27 Oct 2015 08:11:20 -0400 Subject: [PATCH 0428/1003] added AvatarList.avatarSessionChanged(sessionUUID, oldUUID) for AC scripts. --- assignment-client/src/avatars/AvatarMixer.cpp | 6 +++--- libraries/avatars/src/AvatarHashMap.cpp | 1 + libraries/avatars/src/AvatarHashMap.h | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index ae6a8247c1..0a455891f9 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -254,10 +254,10 @@ void AvatarMixer::broadcastAvatarData() { // potentially update the max full rate distance for this frame maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - if (distanceToAvatar != 0.0f + if (distanceToAvatar != 0.0f && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { - return; - } + return; + } AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index c02fe98b19..ddc9160041 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -141,4 +141,5 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID) { void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID) { _lastOwnerSessionUUID = oldUUID; + emit avatarSessionChangedEvent(sessionUUID, oldUUID); } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index f90c61ec4d..c0c511bc3a 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -36,6 +36,7 @@ public: signals: void avatarAddedEvent(const QUuid& sessionUUID); void avatarRemovedEvent(const QUuid& sessionUUID); + void avatarSessionChangedEvent(const QUuid& sessionUUID,const QUuid& oldUUID); public slots: bool isAvatarInRange(const glm::vec3 & position, const float range); From 983c551ce35c0ff5c153d31fa4294a89f38d422c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 27 Oct 2015 09:13:16 -0700 Subject: [PATCH 0429/1003] 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 0430/1003] 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 0431/1003] 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 4c64da9ce5d22ccf6d98b0dc34a5debd9222130f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 27 Oct 2015 10:27:32 -0700 Subject: [PATCH 0432/1003] try to avoid negative roll-over when moving to or from server time --- libraries/physics/src/ObjectAction.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 8ed06aea5d..ff8382a143 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -247,15 +247,29 @@ bool ObjectAction::lifetimeIsOver() { } quint64 ObjectAction::localTimeToServerTime(quint64 timeValue) const { + // 0 indicates no expiration if (timeValue == 0) { return 0; } - return timeValue + getEntityServerClockSkew(); + + int serverClockSkew = getEntityServerClockSkew(); + if (serverClockSkew < 0 && timeValue <= (quint64)(-serverClockSkew)) { + return 1; // non-zero but long-expired value to avoid negative roll-over + } + + return timeValue + serverClockSkew; } quint64 ObjectAction::serverTimeToLocalTime(quint64 timeValue) const { + // 0 indicates no expiration if (timeValue == 0) { return 0; } - return timeValue - getEntityServerClockSkew(); + + int serverClockSkew = getEntityServerClockSkew(); + if (serverClockSkew > 0 && timeValue <= (quint64)serverClockSkew) { + return 1; // non-zero but long-expired value to avoid negative roll-over + } + + return timeValue - serverClockSkew; } From 8a85468254d5cdbbc79a38528ed4e78cf25453eb Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 10:39:44 -0700 Subject: [PATCH 0433/1003] changing model, repositioning --- .../whiteboard/whiteboardEntityScript.js | 2 +- .../painting/whiteboard/whiteboardSpawner.js | 241 ++++++++---------- 2 files changed, 107 insertions(+), 136 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index f38073f389..2d34fc8c40 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -239,7 +239,7 @@ unload: function() { Overlays.deleteOverlay(this.laserPointer); - // this.eraseBoard(); + this.eraseBoard(); } }; diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index cbc26da670..0804b992c7 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -16,13 +16,16 @@ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); +//var modelURL = "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx"; +var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); -center.y += 0.4; + +var whiteboardDimensions, colorIndicator, eraseAllText +var colorBoxes = []; var colors = [ - hexToRgb("#2F8E84"), hexToRgb("#66CCB3"), hexToRgb("#A43C37"), hexToRgb("#491849"), @@ -30,21 +33,13 @@ var colors = [ hexToRgb("#993369"), hexToRgb("#9B47C2") ]; - -//WHITEBOARD -var whiteboardDimensions = { - x: 2, - y: 1.5, - z: 0.08 -}; var whiteboard = Entities.addEntity({ type: "Model", - modelURL: "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx", + modelURL: modelURL, name: "whiteboard", position: center, rotation: rotation, script: scriptURL, - dimensions: whiteboardDimensions, color: { red: 255, green: 255, @@ -52,144 +47,119 @@ var whiteboard = Entities.addEntity({ } }); +Script.setTimeout(function() { + whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions; + setUp(); +}, 500) -// COLOR INDICATOR BOX -var colorIndicatorDimensions = { - x: whiteboardDimensions.x, - y: 0.05, - z: 0.02 -}; -scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); -var colorIndicatorPosition = Vec3.sum(center, { - x: 0, - y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2, - z: 0 -}); -var colorIndicatorBox = Entities.addEntity({ - type: "Box", - name: "Color Indicator", - color: colors[0], - rotation: rotation, - position: colorIndicatorPosition, - dimensions: colorIndicatorDimensions, - script: scriptURL, - userData: JSON.stringify({ - whiteboard: whiteboard - }) -}); -Entities.editEntity(whiteboard, { - userData: JSON.stringify({ - color: { - currentColor: colors[0] - }, - colorIndicator: colorIndicatorBox - }) -}); - -//COLOR BOXES -var direction = Quat.getRight(rotation); -var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); -var colorBoxes = []; -var colorSquareDimensions = { - x: (whiteboardDimensions.x / 2) / (colors.length - 1), - y: 0.1, - z: 0.05 -}; -colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2; -var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); -var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); -for (var i = 0; i < colors.length; i++) { - var colorBox = Entities.addEntity({ +function setUp() { + // COLOR INDICATOR BOX + var colorIndicatorDimensions = { + x: whiteboardDimensions.x, + y: 0.05, + z: 0.02 + }; + scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); + var colorIndicatorPosition = Vec3.sum(center, { + x: 0, + y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2, + z: 0 + }); + colorIndicatorBox = Entities.addEntity({ type: "Box", - name: "Color Selector", - position: colorBoxPosition, - dimensions: colorSquareDimensions, + name: "Color Indicator", + color: colors[0], rotation: rotation, - color: colors[i], + position: colorIndicatorPosition, + dimensions: colorIndicatorDimensions, script: scriptURL, userData: JSON.stringify({ - whiteboard: whiteboard, + whiteboard: whiteboard + }) + }); + + Entities.editEntity(whiteboard, { + userData: JSON.stringify({ + color: { + currentColor: colors[0] + }, colorIndicator: colorIndicatorBox }) }); - colorBoxes.push(colorBox); - colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); + + //COLOR BOXES + var direction = Quat.getRight(rotation); + var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); + var colorSquareDimensions = { + x: 0.1, + y: 0.1, + z: 0.002 + }; + colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2; + var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); + var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); + for (var i = 0; i < colors.length; i++) { + var colorBox = Entities.addEntity({ + type: "Box", + name: "Color Selector", + position: colorBoxPosition, + dimensions: colorSquareDimensions, + rotation: rotation, + color: colors[i], + script: scriptURL, + userData: JSON.stringify({ + whiteboard: whiteboard, + colorIndicator: colorIndicatorBox + }) + }); + colorBoxes.push(colorBox); + colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); + } + + + + var eraseBoxDimensions = { + x: 0.5, + y: 0.1, + z: 0.01 + }; + + + var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01)); + eraseBoxPosition.y += 0.3; + scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); + eraseAllText = Entities.addEntity({ + type: "Text", + position: eraseBoxPosition, + name: "Eraser", + script: scriptURL, + rotation: rotation, + dimensions: eraseBoxDimensions, + backgroundColor: { + red: 0, + green: 60, + blue: 0 + }, + textColor: { + red: 255, + green: 10, + blue: 10 + }, + text: "ERASE BOARD", + lineHeight: 0.07, + userData: JSON.stringify({ + whiteboard: whiteboard + }) + }); + + + } - -// BLACK BOX -var blackBoxDimensions = { - x: 0.3, - y: 0.3, - z: 0.01 -}; - -colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + blackBoxDimensions.x / 2 - 0.01)); -colorBoxPosition.y += 0.3; -var fragShaderURL = Script.resolvePath('blackInk.fs?v1' + Math.random()); -var blackBox = Entities.addEntity({ - type: 'Box', - name: "Black Color", - position: colorBoxPosition, - dimensions: blackBoxDimensions, - rotation: rotation, - color: { - red: 0, - green: 0, - blue: 0 - }, - script: scriptURL, - userData: JSON.stringify({ - whiteboard: whiteboard, - version: 2, - ProceduralEntity: { - shaderUrl: fragShaderURL - } - }) -}); - - -var eraseBoxDimensions = { - x: 0.5, - y: 0.1, - z: 0.01 -}; - - -var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01)); -eraseBoxPosition.y += 0.3; -scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); -var eraseAllText = Entities.addEntity({ - type: "Text", - position: eraseBoxPosition, - name: "Eraser", - script: scriptURL, - rotation: rotation, - dimensions: eraseBoxDimensions, - backgroundColor: { - red: 0, - green: 60, - blue: 0 - }, - textColor: { - red: 255, - green: 10, - blue: 10 - }, - text: "ERASE BOARD", - lineHeight: 0.07, - userData: JSON.stringify({ - whiteboard: whiteboard - }) -}); - - - function cleanup() { Entities.deleteEntity(whiteboard); Entities.deleteEntity(eraseAllText); - Entities.deleteEntity(blackBox); Entities.deleteEntity(colorIndicatorBox); colorBoxes.forEach(function(colorBox) { Entities.deleteEntity(colorBox); @@ -197,5 +167,6 @@ function cleanup() { } + // Uncomment this line to delete whiteboard and all associated entity on script close Script.scriptEnding.connect(cleanup); \ No newline at end of file From 596b315286bdf2de3ce0d627756f5c0a468a2d95 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Tue, 27 Oct 2015 11:35:30 -0700 Subject: [PATCH 0434/1003] 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 fa22249f7a25a822791a20ae186829ef11630713 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 12:03:30 -0700 Subject: [PATCH 0435/1003] Improving model --- .../painting/whiteboard/whiteboardSpawner.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 0804b992c7..2de9821810 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -31,7 +31,7 @@ var colors = [ hexToRgb("#491849"), hexToRgb("#6AB03B"), hexToRgb("#993369"), - hexToRgb("#9B47C2") + hexToRgb("#000000") ]; var whiteboard = Entities.addEntity({ type: "Model", @@ -92,12 +92,19 @@ function setUp() { var direction = Quat.getRight(rotation); var colorBoxPosition = Vec3.subtract(center, Vec3.multiply(direction, whiteboardDimensions.x / 2)); var colorSquareDimensions = { - x: 0.1, - y: 0.1, + x: 0.13, + y: 0.13, z: 0.002 }; - colorBoxPosition.y += whiteboardDimensions.y / 2 + colorIndicatorDimensions.y + colorSquareDimensions.y / 2; - var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 2); + + var palleteDepthOffset = -0.06; + var palleteHeightOffset = -0.28; + + colorBoxPosition = Vec3.sum(colorBoxPosition, Vec3.multiply(palleteDepthOffset, Quat.getFront(rotation))); + colorBoxPosition.y += palleteHeightOffset; + var spaceBetweenColorBoxes = Vec3.multiply(direction, colorSquareDimensions.x * 1.76); + var palleteXOffset = Vec3.multiply(direction, 0.43); + colorBoxPosition = Vec3.sum(colorBoxPosition, palleteXOffset); var scriptURL = Script.resolvePath("colorSelectorEntityScript.js"); for (var i = 0; i < colors.length; i++) { var colorBox = Entities.addEntity({ From f0a1335957aa0a80d61ecca1ddf98f03c4b03c4d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 12:09:07 -0700 Subject: [PATCH 0436/1003] fixed paint initially showing up white --- examples/painting/whiteboard/whiteboardSpawner.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 2de9821810..6a4f800441 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -44,7 +44,12 @@ var whiteboard = Entities.addEntity({ red: 255, green: 255, blue: 255 - } + }, + userData: JSON.stringify({ + color: { + currentColor: colors[0] + } + }) }); Script.setTimeout(function() { From 3742c169b531e133d9c3462ecaf8b3f438c1be2b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 22 Oct 2015 12:03:19 -0700 Subject: [PATCH 0437/1003] Make handler generators more readable --- libraries/script-engine/src/ScriptEngine.cpp | 47 +++++++++----------- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 76590f266b..a317f2a82c 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -508,26 +508,29 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& // Connect up ALL the handlers to the global entities object's signals. // (We could go signal by signal, or even handler by handler, but I don't think the efficiency is worth the complexity.) auto entities = DependencyManager::get(); - connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, - [=](const EntityItemID& entityID) { - _registeredHandlers.remove(entityID); - }); - + connect(entities.data(), &EntityScriptingInterface::deletingEntity, this, [this](const EntityItemID& entityID) { + _registeredHandlers.remove(entityID); + }); + // Two common cases of event handler, differing only in argument signature. - auto makeSingleEntityHandler = [=](const QString& eventName) -> std::function { - return [=](const EntityItemID& entityItemID) -> void { - generalHandler(entityItemID, eventName, [=]() -> QScriptValueList { - return QScriptValueList() << entityItemID.toScriptValue(this); - }); + auto makeSingleEntityHandler = [&](QString eventName) { + return [this, eventName](const EntityItemID& entityItemID) { + forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) }); }; }; - auto makeMouseHandler = [=](const QString& eventName) -> std::function { - return [=](const EntityItemID& entityItemID, const MouseEvent& event) -> void { - generalHandler(entityItemID, eventName, [=]() -> QScriptValueList { - return QScriptValueList() << entityItemID.toScriptValue(this) << event.toScriptValue(this); - }); + auto makeMouseHandler = [&](QString eventName) { + return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) { + forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); }; }; + + auto makeCollisionHandler = [&](QString eventName) { + return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { + forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this), + collisionToScriptValue(this, collision) }); + }; + }; + connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity")); connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity")); @@ -543,12 +546,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity")); connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity")); - connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, - [=](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { - generalHandler(idA, "collisionWithEntity", [=]() { - return QScriptValueList () << idA.toScriptValue(this) << idB.toScriptValue(this) << collisionToScriptValue(this, collision); - }); - }); + connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity")); } if (!_registeredHandlers.contains(entityID)) { _registeredHandlers[entityID] = RegisteredEventHandlers(); @@ -899,9 +897,9 @@ 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) { +void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { - qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; + qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; assert(false); return; } @@ -915,9 +913,8 @@ void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& e } QScriptValueList handlersForEvent = handlersOnEntity[eventName]; if (!handlersForEvent.isEmpty()) { - QScriptValueList args = argGenerator(); for (int i = 0; i < handlersForEvent.count(); ++i) { - handlersForEvent[i].call(QScriptValue(), args); + handlersForEvent[i].call(QScriptValue(), eventHanderArgs); } } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1d3986143a..c2f9d966f1 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -193,7 +193,7 @@ private: ArrayBufferClass* _arrayBufferClass; QHash _registeredHandlers; - void generalHandler(const EntityItemID& entityID, const QString& eventName, std::function argGenerator); + void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs); Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success); static QSet _allKnownScriptEngines; From c8c9118d4d0f671ea7f7cc4ed9dc64bdb4eaae30 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 23 Oct 2015 15:40:56 -0700 Subject: [PATCH 0438/1003] Improve script engine error logging + some cleanup --- libraries/script-engine/src/ScriptEngine.cpp | 74 ++++++++++++++------ libraries/script-engine/src/ScriptEngine.h | 5 +- libraries/shared/src/LogHandler.cpp | 2 +- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a317f2a82c..cbe9449551 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -556,7 +556,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& } -QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { +QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { if (_stoppingAllScripts) { return QScriptValue(); // bail early } @@ -565,27 +565,30 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN QScriptValue result; #ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::evaluate() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "program:" << program << " fileName:" << fileName << "lineNumber:" << lineNumber; + "sourceCode:" << sourceCode << " fileName:" << fileName << "lineNumber:" << lineNumber; #endif QMetaObject::invokeMethod(this, "evaluate", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QScriptValue, result), - Q_ARG(const QString&, program), + Q_ARG(const QString&, sourceCode), Q_ARG(const QString&, fileName), Q_ARG(int, lineNumber)); return result; } + + // Check synthax + const QScriptProgram program(sourceCode, fileName, lineNumber); + if (!checkSynthax(program)) { + return QScriptValue(); + } - _evaluatesPending++; - QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); - } - _evaluatesPending--; + ++_evaluatesPending; + const auto result = QScriptEngine::evaluate(program); + --_evaluatesPending; + + const auto hadUncaughtException = checkExceptions(this, program.fileName()); if (_wantSignals) { - emit evaluationFinished(result, hasUncaughtException()); + emit evaluationFinished(result, hadUncaughtException); } - clearExceptions(); return result; } @@ -603,7 +606,7 @@ void ScriptEngine::run() { emit runningStateChanged(); } - QScriptValue result = evaluate(_scriptContents); + QScriptValue result = evaluate(_scriptContents, _fileNameString); QElapsedTimer startTime; startTime.start(); @@ -644,15 +647,6 @@ void ScriptEngine::run() { qint64 now = usecTimestampNow(); float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND; - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString(); - if (_wantSignals) { - emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString()); - } - clearExceptions(); - } - if (!_isFinished) { if (_wantSignals) { emit update(deltaTime); @@ -660,6 +654,7 @@ void ScriptEngine::run() { } lastUpdate = now; + checkExceptions(this, _fileNameString); } stopAllTimers(); // make sure all our timers are stopped if the script is ending @@ -896,6 +891,38 @@ void ScriptEngine::load(const QString& loadFile) { } } + +bool ScriptEngine::checkSynthax(const QScriptProgram& program) { + const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); + if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { + const auto error = syntaxCheck.errorMessage(); + const auto line = QString::number(syntaxCheck.errorLineNumber()); + const auto column = QString::number(syntaxCheck.errorColumnNumber()); + const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + +bool ScriptEngine::checkExceptions(QScriptEngine* engine, const QString& fileName) { + if (engine->hasUncaughtException()) { + const auto backtrace = engine->uncaughtExceptionBacktrace(); + const auto exception = engine->uncaughtException().toString(); + const auto line = QString::number(engine->uncaughtExceptionLineNumber()); + engine->clearExceptions(); + + auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); + if (!backtrace.empty()) { + static const auto lineSeparator = "\n "; + message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); + } + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { @@ -1015,7 +1042,8 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch(); } - QScriptValue entityScriptConstructor = evaluate(contents); + auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); + QScriptValue entityScriptConstructor = evaluate(contents, fileName); QScriptValue entityScriptObject = entityScriptConstructor.construct(); EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified }; _entityScripts[entityID] = newDetails; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index c2f9d966f1..8fda876a31 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -88,7 +88,7 @@ public: Q_INVOKABLE void registerValue(const QString& valueName, QScriptValue value); /// evaluate some code in the context of the ScriptEngine and return the result - Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); // this is also used by the script tool widget + Q_INVOKABLE QScriptValue evaluate(const QString& program, const QString& fileName, int lineNumber = 1); // this is also used by the script tool widget /// if the script engine is not already running, this will download the URL and start the process of seting it up /// to run... NOTE - this is used by Application currently to load the url. We don't really want it to be exposed @@ -182,6 +182,9 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); + static bool checkSynthax(const QScriptProgram& program); + static bool checkExceptions(QScriptEngine* engine, const QString& fileName); + AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; Quat _quatLibrary; diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index 22ea12c1b3..cc3519e43e 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -135,7 +135,7 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont prefixString.append(QString(" [%1]").arg(_targetName)); } - QString logMessage = QString("%1 %2").arg(prefixString, message); + QString logMessage = QString("%1 %2").arg(prefixString, message.split("\n").join("\n" + prefixString + " ")); fprintf(stdout, "%s\n", qPrintable(logMessage)); return logMessage; } From 13b7fa6b5df805497795725556f06cf0b9294731 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 10:49:41 -0700 Subject: [PATCH 0439/1003] Typo --- libraries/script-engine/src/ScriptEngine.cpp | 6 +++--- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index cbe9449551..2c725bdf70 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -575,9 +575,9 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi return result; } - // Check synthax + // Check syntax const QScriptProgram program(sourceCode, fileName, lineNumber); - if (!checkSynthax(program)) { + if (!checkSyntax(program)) { return QScriptValue(); } @@ -892,7 +892,7 @@ void ScriptEngine::load(const QString& loadFile) { } -bool ScriptEngine::checkSynthax(const QScriptProgram& program) { +bool ScriptEngine::checkSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { const auto error = syntaxCheck.errorMessage(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 8fda876a31..db10ecef40 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -182,7 +182,7 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - static bool checkSynthax(const QScriptProgram& program); + static bool checkSyntax(const QScriptProgram& program); static bool checkExceptions(QScriptEngine* engine, const QString& fileName); AbstractControllerScriptingInterface* _controllerScriptingInterface; From 3e43a3c345b0f06f7789dbf57bb8885e260aba87 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 11:33:13 -0700 Subject: [PATCH 0440/1003] Some more script checks --- libraries/script-engine/src/ScriptEngine.cpp | 26 +++++++------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2c725bdf70..0411c507ab 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -79,7 +79,6 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { return engine->newQObject(in); } - void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { out = qobject_cast(object.toQObject()); } @@ -95,9 +94,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _wantSignals(wantSignals), _controllerScriptingInterface(controllerScriptingInterface), _fileNameString(fileNameString), - _quatLibrary(), - _vec3Library(), - _uuidLibrary(), _isUserLoaded(false), _isReloading(false), _arrayBufferClass(new ArrayBufferClass(this)) @@ -654,7 +650,9 @@ void ScriptEngine::run() { } lastUpdate = now; - checkExceptions(this, _fileNameString); + if (!checkExceptions(this, _fileNameString)) { + stop(); + } } stopAllTimers(); // make sure all our timers are stopped if the script is ending @@ -891,7 +889,6 @@ void ScriptEngine::load(const QString& loadFile) { } } - bool ScriptEngine::checkSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { @@ -1005,14 +1002,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co auto scriptCache = DependencyManager::get(); bool isFileUrl = isURL && scriptOrURL.startsWith("file://"); + auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); - // first check the syntax of the script contents - QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(contents); - if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { - qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID; - qCDebug(scriptengine) << " " << syntaxCheck.errorMessage() << ":" - << syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber(); - qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL; + QScriptProgram program(contents, fileName); + if (!checkSyntax(program)) { if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1027,9 +1020,9 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co QScriptValue testConstructor = sandbox.evaluate(contents); if (!testConstructor.isFunction()) { - qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID; - qCDebug(scriptengine) << " NOT CONSTRUCTOR"; - qCDebug(scriptengine) << " SCRIPT:" << scriptOrURL; + qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n" + " NOT CONSTRUCTOR\n" + " SCRIPT:" << scriptOrURL; if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1042,7 +1035,6 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co lastModified = (quint64)QFileInfo(file).lastModified().toMSecsSinceEpoch(); } - auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); QScriptValue entityScriptConstructor = evaluate(contents, fileName); QScriptValue entityScriptObject = entityScriptConstructor.construct(); EntityScriptDetails newDetails = { scriptOrURL, entityScriptObject, lastModified }; From 1bebdc8e7022890310e87e73e29521ed2b4df53c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 11:33:36 -0700 Subject: [PATCH 0441/1003] Fix scripts syntax errors --- examples/libraries/entityCameraTool.js | 10 +++++----- examples/libraries/soundArray.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index d304a6382e..88e01b29fe 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -564,12 +564,12 @@ CameraTool = function(cameraManager) { var ORIENTATION_OVERLAY_SIZE = 26; var ORIENTATION_OVERLAY_HALF_SIZE = ORIENTATION_OVERLAY_SIZE / 2; - var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5, + var ORIENTATION_OVERLAY_CUBE_SIZE = 10.5; - var ORIENTATION_OVERLAY_OFFSET = { - x: 30, - y: 30, - } + var ORIENTATION_OVERLAY_OFFSET = { + x: 30, + y: 30, + } var UI_WIDTH = 70; var UI_HEIGHT = 70; diff --git a/examples/libraries/soundArray.js b/examples/libraries/soundArray.js index 813621fb4b..f59c88a723 100644 --- a/examples/libraries/soundArray.js +++ b/examples/libraries/soundArray.js @@ -6,7 +6,7 @@ SoundArray = function(audioOptions, autoUpdateAudioPosition) { this.audioOptions = audioOptions !== undefined ? audioOptions : {}; this.autoUpdateAudioPosition = autoUpdateAudioPosition !== undefined ? autoUpdateAudioPosition : false; if (this.audioOptions.position === undefined) { - this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0}), + this.audioOptions.position = Vec3.sum(MyAvatar.position, { x: 0, y: 1, z: 0}); } if (this.audioOptions.volume === undefined) { this.audioOptions.volume = 1.0; From ea56f965a48ddc368f2e38ccf9b10dd2364baee4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 12:27:48 -0700 Subject: [PATCH 0442/1003] Lambda conversion fix for windows --- libraries/script-engine/src/ScriptEngine.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0411c507ab..bcfeef1acb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -509,18 +509,22 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }); // Two common cases of event handler, differing only in argument signature. - auto makeSingleEntityHandler = [&](QString eventName) { + using SingleEntityHandler = std::function; + auto makeSingleEntityHandler = [this](QString eventName) -> SingleEntityHandler { return [this, eventName](const EntityItemID& entityItemID) { forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this) }); }; }; - auto makeMouseHandler = [&](QString eventName) { + + using MouseHandler = std::function; + auto makeMouseHandler = [this](QString eventName) -> MouseHandler { return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) { forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); }; }; - auto makeCollisionHandler = [&](QString eventName) { + using CollisionHandler = std::function; + auto makeCollisionHandler = [this](QString eventName) -> CollisionHandler { return [this, eventName](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { forwardHandlerCall(idA, eventName, { idA.toScriptValue(this), idB.toScriptValue(this), collisionToScriptValue(this, collision) }); From a53a576aa32901ab4d5af7e4fa45cab99f30f1e9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 26 Oct 2015 12:33:42 -0700 Subject: [PATCH 0443/1003] checkExceptions after testing entity scripts in the sandbox --- libraries/script-engine/src/ScriptEngine.cpp | 23 +++++++++++--------- libraries/script-engine/src/ScriptEngine.h | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index bcfeef1acb..986613c20b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -585,7 +585,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; - const auto hadUncaughtException = checkExceptions(this, program.fileName()); + const auto hadUncaughtException = checkExceptions(*this, program.fileName()); if (_wantSignals) { emit evaluationFinished(result, hadUncaughtException); } @@ -654,7 +654,7 @@ void ScriptEngine::run() { } lastUpdate = now; - if (!checkExceptions(this, _fileNameString)) { + if (!checkExceptions(*this, _fileNameString)) { stop(); } } @@ -906,12 +906,12 @@ bool ScriptEngine::checkSyntax(const QScriptProgram& program) { return true; } -bool ScriptEngine::checkExceptions(QScriptEngine* engine, const QString& fileName) { - if (engine->hasUncaughtException()) { - const auto backtrace = engine->uncaughtExceptionBacktrace(); - const auto exception = engine->uncaughtException().toString(); - const auto line = QString::number(engine->uncaughtExceptionLineNumber()); - engine->clearExceptions(); +bool ScriptEngine::checkExceptions(QScriptEngine& engine, const QString& fileName) { + if (engine.hasUncaughtException()) { + const auto backtrace = engine.uncaughtExceptionBacktrace(); + const auto exception = engine.uncaughtException().toString(); + const auto line = QString::number(engine.uncaughtExceptionLineNumber()); + engine.clearExceptions(); auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); if (!backtrace.empty()) { @@ -1021,8 +1021,11 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co } QScriptEngine sandbox; - QScriptValue testConstructor = sandbox.evaluate(contents); - + QScriptValue testConstructor = sandbox.evaluate(program); + if (!checkExceptions(sandbox, program.fileName())) { + return; + } + if (!testConstructor.isFunction()) { qCDebug(scriptengine) << "ScriptEngine::loadEntityScript() entity:" << entityID << "\n" " NOT CONSTRUCTOR\n" diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index db10ecef40..ddd957e163 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -183,7 +183,7 @@ private: void stopTimer(QTimer* timer); static bool checkSyntax(const QScriptProgram& program); - static bool checkExceptions(QScriptEngine* engine, const QString& fileName); + static bool checkExceptions(QScriptEngine& engine, const QString& fileName); AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; From ffe73348fb9157186005b9e93e8ef24cb96a5a99 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 Oct 2015 12:10:45 -0700 Subject: [PATCH 0444/1003] Move check functions to cpp only --- libraries/script-engine/src/ScriptEngine.cpp | 62 ++++++++++---------- libraries/script-engine/src/ScriptEngine.h | 3 - 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 986613c20b..92bc3da595 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -83,6 +83,37 @@ void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputCon out = qobject_cast(object.toQObject()); } +bool checkSyntax(const QScriptProgram& program) { + const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); + if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { + const auto error = syntaxCheck.errorMessage(); + const auto line = QString::number(syntaxCheck.errorLineNumber()); + const auto column = QString::number(syntaxCheck.errorColumnNumber()); + const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + +bool checkExceptions(QScriptEngine& engine, const QString& fileName) { + if (engine.hasUncaughtException()) { + const auto backtrace = engine.uncaughtExceptionBacktrace(); + const auto exception = engine.uncaughtException().toString(); + const auto line = QString::number(engine.uncaughtExceptionLineNumber()); + engine.clearExceptions(); + + auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); + if (!backtrace.empty()) { + static const auto lineSeparator = "\n "; + message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); + } + qCWarning(scriptengine) << qPrintable(message); + return false; + } + return true; +} + ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) : @@ -893,37 +924,6 @@ void ScriptEngine::load(const QString& loadFile) { } } -bool ScriptEngine::checkSyntax(const QScriptProgram& program) { - const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); - if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { - const auto error = syntaxCheck.errorMessage(); - const auto line = QString::number(syntaxCheck.errorLineNumber()); - const auto column = QString::number(syntaxCheck.errorColumnNumber()); - const auto message = QString("[SyntaxError] %1 in %2:%3(%4)").arg(error, program.fileName(), line, column); - qCWarning(scriptengine) << qPrintable(message); - return false; - } - return true; -} - -bool ScriptEngine::checkExceptions(QScriptEngine& engine, const QString& fileName) { - if (engine.hasUncaughtException()) { - const auto backtrace = engine.uncaughtExceptionBacktrace(); - const auto exception = engine.uncaughtException().toString(); - const auto line = QString::number(engine.uncaughtExceptionLineNumber()); - engine.clearExceptions(); - - auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line); - if (!backtrace.empty()) { - static const auto lineSeparator = "\n "; - message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); - } - qCWarning(scriptengine) << qPrintable(message); - return false; - } - return true; -} - // Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs) { if (QThread::currentThread() != thread()) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ddd957e163..89d651930b 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -181,9 +181,6 @@ private: QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); void stopTimer(QTimer* timer); - - static bool checkSyntax(const QScriptProgram& program); - static bool checkExceptions(QScriptEngine& engine, const QString& fileName); AbstractControllerScriptingInterface* _controllerScriptingInterface; QString _fileNameString; From 8fcc07102699807e6eee5392c6765df9076e3ade Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 12:24:29 -0700 Subject: [PATCH 0445/1003] only can draw on whiteboard --- .../painting/whiteboard/whiteboardSpawner.js | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 6a4f800441..366aa2b73d 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -33,24 +33,32 @@ var colors = [ hexToRgb("#993369"), hexToRgb("#000000") ]; + var whiteboard = Entities.addEntity({ type: "Model", modelURL: modelURL, - name: "whiteboard", + name: "whiteboard base", position: center, rotation: rotation, +}); + +var surfaceCenter = Vec3.sum(center, Vec3.multiply(-0.1, Quat.getFront(rotation))); +surfaceCenter.y += 0.55; +var drawingSurface = Entities.addEntity({ + type: "Box", + color: {red: 255, green: 255, blue: 255}, + name: "whiteboard surface", + position: surfaceCenter, + dimensions: {x: 1.8, y: 1.4, z: 0.01}, script: scriptURL, - color: { - red: 255, - green: 255, - blue: 255 - }, - userData: JSON.stringify({ + rotation: rotation, + userData: JSON.stringify({ color: { currentColor: colors[0] } }) -}); + +}) Script.setTimeout(function() { whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions; @@ -80,11 +88,11 @@ function setUp() { dimensions: colorIndicatorDimensions, script: scriptURL, userData: JSON.stringify({ - whiteboard: whiteboard + whiteboard: drawingSurface }) }); - Entities.editEntity(whiteboard, { + Entities.editEntity(drawingSurface, { userData: JSON.stringify({ color: { currentColor: colors[0] @@ -121,7 +129,7 @@ function setUp() { color: colors[i], script: scriptURL, userData: JSON.stringify({ - whiteboard: whiteboard, + whiteboard: drawingSurface, colorIndicator: colorIndicatorBox }) }); @@ -161,7 +169,7 @@ function setUp() { text: "ERASE BOARD", lineHeight: 0.07, userData: JSON.stringify({ - whiteboard: whiteboard + whiteboard: drawingSurface }) }); @@ -171,6 +179,7 @@ function setUp() { function cleanup() { Entities.deleteEntity(whiteboard); + Entities.deleteEntity(drawingSurface); Entities.deleteEntity(eraseAllText); Entities.deleteEntity(colorIndicatorBox); colorBoxes.forEach(function(colorBox) { From c0138bd189542116b323c1e75c75f68b7afea8c1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 Oct 2015 12:39:40 -0700 Subject: [PATCH 0446/1003] Rename check functions and make them static --- libraries/script-engine/src/ScriptEngine.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 92bc3da595..1d6bf32fcc 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -83,7 +83,7 @@ void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputCon out = qobject_cast(object.toQObject()); } -bool checkSyntax(const QScriptProgram& program) { +static bool hasCorrectSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { const auto error = syntaxCheck.errorMessage(); @@ -96,7 +96,7 @@ bool checkSyntax(const QScriptProgram& program) { return true; } -bool checkExceptions(QScriptEngine& engine, const QString& fileName) { +static bool hadUncauchtExceptions(QScriptEngine& engine, const QString& fileName) { if (engine.hasUncaughtException()) { const auto backtrace = engine.uncaughtExceptionBacktrace(); const auto exception = engine.uncaughtException().toString(); @@ -109,9 +109,9 @@ bool checkExceptions(QScriptEngine& engine, const QString& fileName) { message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator)); } qCWarning(scriptengine) << qPrintable(message); - return false; + return true; } - return true; + return false; } ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, @@ -608,7 +608,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi // Check syntax const QScriptProgram program(sourceCode, fileName, lineNumber); - if (!checkSyntax(program)) { + if (!hasCorrectSyntax(program)) { return QScriptValue(); } @@ -616,7 +616,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; - const auto hadUncaughtException = checkExceptions(*this, program.fileName()); + const auto hadUncaughtException = hadUncauchtExceptions(*this, program.fileName()); if (_wantSignals) { emit evaluationFinished(result, hadUncaughtException); } @@ -685,7 +685,7 @@ void ScriptEngine::run() { } lastUpdate = now; - if (!checkExceptions(*this, _fileNameString)) { + if (hadUncauchtExceptions(*this, _fileNameString)) { stop(); } } @@ -1009,7 +1009,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co auto fileName = QString("(EntityID:%1, %2)").arg(entityID.toString(), isURL ? scriptOrURL : "EmbededEntityScript"); QScriptProgram program(contents, fileName); - if (!checkSyntax(program)) { + if (!hasCorrectSyntax(program)) { if (!isFileUrl) { scriptCache->addScriptToBadScriptList(scriptOrURL); } @@ -1022,7 +1022,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co QScriptEngine sandbox; QScriptValue testConstructor = sandbox.evaluate(program); - if (!checkExceptions(sandbox, program.fileName())) { + if (hadUncauchtExceptions(sandbox, program.fileName())) { return; } From 25ded2bd9212f5c26a5285c64a79e8195fd18e16 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 13:03:44 -0700 Subject: [PATCH 0447/1003] added shapetype --- examples/painting/whiteboard/whiteboardSpawner.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 366aa2b73d..a7cfc0719a 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -36,6 +36,7 @@ var colors = [ var whiteboard = Entities.addEntity({ type: "Model", + shapeType: "box", modelURL: modelURL, name: "whiteboard base", position: center, From 0512da0e3971c21c57cb5c8e1233e3ba801004b2 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 13:07:11 -0700 Subject: [PATCH 0448/1003] updated url --- examples/painting/whiteboard/whiteboardSpawner.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index a7cfc0719a..16953129af 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -17,7 +17,8 @@ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); //var modelURL = "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx"; -var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); +//var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx"; var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); From a1f1e504319c49734dcd51cd06277cd747fff87b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 21 Oct 2015 17:37:26 -0700 Subject: [PATCH 0449/1003] split CharacterController btw phys and interface --- interface/src/Application.cpp | 5 +- interface/src/avatar/MyAvatar.cpp | 109 +++++++++----- interface/src/avatar/MyAvatar.h | 43 ++++-- .../src/avatar/MyAvatarController.cpp | 137 ++++++++++++------ interface/src/avatar/MyAvatarController.h | 106 ++++++++++++++ libraries/physics/src/CharacterController.cpp | 52 +++++++ libraries/physics/src/CharacterController.h | 60 ++++++++ .../physics/src/DynamicCharacterController.h | 95 ------------ libraries/physics/src/PhysicsEngine.cpp | 43 +++--- libraries/physics/src/PhysicsEngine.h | 7 +- 10 files changed, 437 insertions(+), 220 deletions(-) rename libraries/physics/src/DynamicCharacterController.cpp => interface/src/avatar/MyAvatarController.cpp (81%) create mode 100644 interface/src/avatar/MyAvatarController.h create mode 100644 libraries/physics/src/CharacterController.cpp create mode 100644 libraries/physics/src/CharacterController.h delete mode 100644 libraries/physics/src/DynamicCharacterController.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1b6be53876..79339b03de 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2756,7 +2756,6 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("physics"); - myAvatar->relayDriveKeysToCharacterController(); static VectorOfMotionStates motionStates; _entitySimulation.getObjectsToDelete(motionStates); @@ -2783,6 +2782,8 @@ void Application::update(float deltaTime) { avatarManager->getObjectsToChange(motionStates); _physicsEngine->changeObjects(motionStates); + myAvatar->prepareForPhysicsSimulation(); + _entities.getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); @@ -2807,6 +2808,8 @@ void Application::update(float deltaTime) { // and will simulate entity motion (the EntityTree has been given an EntitySimulation). _entities.update(); // update the models... } + + myAvatar->harvestResultsFromPhysicsSimulation(); } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5920543dca..e52e56cd89 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -109,8 +109,13 @@ MyAvatar::MyAvatar(RigPointer rig) : _goToOrientation(), _rig(rig), _prevShouldDrawHead(true), - _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()) + _audioListenerMode(FROM_HEAD) +#ifdef OLD_HMD_TRACKER + ,_hmdAtRestDetector(glm::vec3(0), glm::quat()) +#else + ,_avatarOffsetFromHMD(0.0f) + ,_hmdVelocity(0.0f) +#endif // OLD_HMD_TRACKER { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -154,7 +159,10 @@ void MyAvatar::reset(bool andReload) { } // Reset dynamic state. - _wasPushing = _isPushing = _isBraking = _billboardValid = _straighteningLean = false; + _wasPushing = _isPushing = _isBraking = _billboardValid = false; +#ifdef OLD_HMD_TRACKER + _isFollowingHMD = false; +#endif // OLD_HMD_TRACKER _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -302,9 +310,10 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrix; } +#ifdef OLD_HMD_TRACKER // returns true if pos is OUTSIDE of the vertical capsule // where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad. -static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRad) { +static bool pointIsOutsideCapsule(const glm::vec3& pos, float capsuleLen, float capsuleRad) { const float halfCapsuleLen = capsuleLen / 2.0f; if (fabs(pos.y) <= halfCapsuleLen) { // cylinder check for middle capsule @@ -320,12 +329,18 @@ static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRa return false; } } +#endif // OLD_HMD_TRACKER // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { + // update the sensorMatrices based on the new hmd pose + _hmdSensorMatrix = hmdSensorMatrix; + _hmdSensorPosition = extractTranslation(hmdSensorMatrix); + _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); +#ifdef OLD_HMD_TRACKER // calc deltaTime auto now = usecTimestampNow(); auto deltaUsecs = now - _lastUpdateFromHMDTime; @@ -334,11 +349,6 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { const float BIGGEST_DELTA_TIME_SECS = 0.25f; float deltaTime = glm::clamp((float)actualDeltaTime, 0.0f, BIGGEST_DELTA_TIME_SECS); - // update the sensorMatrices based on the new hmd pose - _hmdSensorMatrix = hmdSensorMatrix; - _hmdSensorPosition = extractTranslation(hmdSensorMatrix); - _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); - bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); // It can be more accurate/smooth to use velocity rather than position, @@ -360,58 +370,57 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; _lastIsMoving = isMoving; - if (shouldBeginStraighteningLean() || hmdIsAtRest || justStartedMoving) { - beginStraighteningLean(); + if (shouldFollowHMD() || hmdIsAtRest || justStartedMoving) { + beginFollowingHMD(); } - processStraighteningLean(deltaTime); + followHMD(deltaTime); +#endif // OLD_HMD_TRACKER } -void MyAvatar::beginStraighteningLean() { +#ifdef OLD_HMD_TRACKER +void MyAvatar::beginFollowingHMD() { // begin homing toward derived body position. - if (!_straighteningLean) { - _straighteningLean = true; - _straighteningLeanAlpha = 0.0f; + if (!_isFollowingHMD) { + _isFollowingHMD = true; + _followHMDAlpha = 0.0f; } } -bool MyAvatar::shouldBeginStraighteningLean() const { - // define a vertical capsule - const float STRAIGHTENING_LEAN_CAPSULE_RADIUS = 0.2f; // meters - const float STRAIGHTENING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. - - // detect if the derived body position is outside of a capsule around the _bodySensorMatrix - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); - glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - bool isBodyPosOutsideCapsule = capsuleCheck(diff, STRAIGHTENING_LEAN_CAPSULE_LENGTH, STRAIGHTENING_LEAN_CAPSULE_RADIUS); - - if (isBodyPosOutsideCapsule) { - return true; - } else { - return false; +bool MyAvatar::shouldFollowHMD() const { + if (!_isFollowingHMD) { + // define a vertical capsule + const float FOLLOW_HMD_CAPSULE_RADIUS = 0.2f; // meters + const float FOLLOW_HMD_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. + + // detect if the derived body position is outside of a capsule around the _bodySensorMatrix + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); + glm::vec3 localPoint = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); + return pointIsOutsideCapsule(localPoint, FOLLOW_HMD_CAPSULE_LENGTH, FOLLOW_HMD_CAPSULE_RADIUS); } + return false; } -void MyAvatar::processStraighteningLean(float deltaTime) { - if (_straighteningLean) { +void MyAvatar::followHMD(float deltaTime) { + if (_isFollowingHMD) { - const float STRAIGHTENING_LEAN_DURATION = 0.5f; // seconds + const float FOLLOW_HMD_DURATION = 0.5f; // seconds auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); - _straighteningLeanAlpha += (1.0f / STRAIGHTENING_LEAN_DURATION) * deltaTime; + _followHMDAlpha += (1.0f / FOLLOW_HMD_DURATION) * deltaTime; - if (_straighteningLeanAlpha >= 1.0f) { - _straighteningLean = false; + if (_followHMDAlpha >= 1.0f) { + _isFollowingHMD = false; nextAttitude(worldBodyPos, worldBodyRot); _bodySensorMatrix = newBodySensorMatrix; } else { // interp position toward the desired pos - glm::vec3 pos = lerp(getPosition(), worldBodyPos, _straighteningLeanAlpha); - glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _straighteningLeanAlpha)); + glm::vec3 pos = lerp(getPosition(), worldBodyPos, _followHMDAlpha); + glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _followHMDAlpha)); nextAttitude(pos, rot); // interp sensor matrix toward desired @@ -419,12 +428,17 @@ void MyAvatar::processStraighteningLean(float deltaTime) { glm::quat nextBodyRot = glm::normalize(glm::quat_cast(newBodySensorMatrix)); glm::vec3 prevBodyPos = extractTranslation(_bodySensorMatrix); glm::quat prevBodyRot = glm::normalize(glm::quat_cast(_bodySensorMatrix)); - pos = lerp(prevBodyPos, nextBodyPos, _straighteningLeanAlpha); - rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _straighteningLeanAlpha)); + pos = lerp(prevBodyPos, nextBodyPos, _followHMDAlpha); + rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _followHMDAlpha)); _bodySensorMatrix = createMatFromQuatAndPos(rot, pos); } } } +#else +void MyAvatar::harvestHMDOffset(glm::vec3 offset) { + +} +#endif // USE_OLD // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. @@ -1274,6 +1288,21 @@ void MyAvatar::rebuildSkeletonBody() { _characterController.setLocalBoundingBox(corner, scale); } +void MyAvatar::prepareForPhysicsSimulation() { + relayDriveKeysToCharacterController(); + _characterController.setTargetVelocity(getTargetVelocity()); + _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); + //_characterController.setHMDVelocity(hmdVelocity); +} + +void MyAvatar::harvestResultsFromPhysicsSimulation() { + glm::vec3 position = getPosition(); + glm::quat orientation = getOrientation(); + _characterController.getAvatarPositionAndOrientation(position, orientation); + nextAttitude(position, orientation); + setVelocity(_characterController.getLinearVelocity()); +} + QString MyAvatar::getScriptedMotorFrame() const { QString frame = "avatar"; if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 7347419fee..f3f55f4a7a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -12,12 +12,16 @@ #ifndef hifi_MyAvatar_h #define hifi_MyAvatar_h +#include + #include -#include #include #include "Avatar.h" -#include "AtRestDetector.h" +//#include "AtRestDetector.h" +#include "MyAvatarController.h" + +//#define OLD_HMD_TRACKER class ModelItemID; @@ -154,8 +158,10 @@ public: virtual void setAttachmentData(const QVector& attachmentData) override; - DynamicCharacterController* getCharacterController() { return &_characterController; } + MyAvatarController* getCharacterController() { return &_characterController; } + void prepareForPhysicsSimulation(); + void harvestResultsFromPhysicsSimulation(); const QString& getCollisionSoundURL() {return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); @@ -262,9 +268,13 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } - void beginStraighteningLean(); - bool shouldBeginStraighteningLean() const; - void processStraighteningLean(float deltaTime); +#ifdef OLD_HMD_TRACKER + void beginFollowingHMD(); + bool shouldFollowHMD() const; + void followHMD(float deltaTime); +#else + void harvestHMDOffset(glm::vec3 offset); +#endif bool cameraInsideHead() const; @@ -295,7 +305,7 @@ private: quint32 _motionBehaviors; QString _collisionSoundURL; - DynamicCharacterController _characterController; + MyAvatarController _characterController; AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; @@ -348,21 +358,26 @@ private: RigPointer _rig; bool _prevShouldDrawHead; - bool _enableDebugDrawBindPose = false; - bool _enableDebugDrawAnimPose = false; - AnimSkeleton::ConstPointer _debugDrawSkeleton = nullptr; + bool _enableDebugDrawBindPose { false }; + bool _enableDebugDrawAnimPose { false }; + AnimSkeleton::ConstPointer _debugDrawSkeleton { nullptr }; AudioListenerMode _audioListenerMode; glm::vec3 _customListenPosition; glm::quat _customListenOrientation; - bool _straighteningLean = false; - float _straighteningLeanAlpha = 0.0f; +#ifdef OLD_HMD_TRACKER + bool _isFollowingHMD { false }; + float _followHMDAlpha{0.0f}; - quint64 _lastUpdateFromHMDTime = usecTimestampNow(); + quint64 _lastUpdateFromHMDTime{usecTimestampNow()}; AtRestDetector _hmdAtRestDetector; glm::vec3 _lastPosition; - bool _lastIsMoving = false; + bool _lastIsMoving { false }; +#else + glm::vec3 _avatarOffsetFromHMD; + glm::vec3 _hmdVelocity; +#endif // OLD_HMD_TRACKER }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/interface/src/avatar/MyAvatarController.cpp similarity index 81% rename from libraries/physics/src/DynamicCharacterController.cpp rename to interface/src/avatar/MyAvatarController.cpp index 604326168c..931c8b9b08 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/interface/src/avatar/MyAvatarController.cpp @@ -1,13 +1,27 @@ +// +// MyAvatar.h +// interface/src/avatar +// +// Created by AndrewMeadows 2015.10.21 +// 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 "MyAvatarController.h" + #include #include #include #include +#include +#include +#include #include -#include "BulletUtil.h" -#include "DynamicCharacterController.h" -#include "PhysicsLogging.h" +#include "MyAvatar.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float DEFAULT_GRAVITY = -5.0f; @@ -15,11 +29,6 @@ const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; -const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; -const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; -const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; -const uint32_t PENDING_FLAG_JUMP = 1U << 3; - // TODO: improve walking up steps // TODO: make avatars able to walk up and down steps/slopes // TODO: make avatars stand on steep slope @@ -41,19 +50,19 @@ protected: btRigidBody* _me; }; -DynamicCharacterController::DynamicCharacterController(AvatarData* avatarData) { +MyAvatarController::MyAvatarController(MyAvatar* avatar) { _halfHeight = 1.0f; _shape = nullptr; - _rigidBody = nullptr; - assert(avatarData); - _avatarData = avatarData; + assert(avatar); + _avatar = avatar; _enabled = false; _floorDistance = MAX_FALL_HEIGHT; - _walkVelocity.setValue(0.0f,0.0f,0.0f); + _walkVelocity.setValue(0.0f, 0.0f, 0.0f); + _hmdVelocity.setValue(0.0f, 0.0f, 0.0f); _jumpSpeed = JUMP_SPEED; _isOnGround = false; _isJumping = false; @@ -61,21 +70,16 @@ DynamicCharacterController::DynamicCharacterController(AvatarData* avatarData) { _isHovering = true; _isPushingUp = false; _jumpToHoverStart = 0; + _lastStepDuration = 0.0f; _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; updateShapeIfNecessary(); } -DynamicCharacterController::~DynamicCharacterController() { +MyAvatarController::~MyAvatarController() { } -// virtual -void DynamicCharacterController::setWalkDirection(const btVector3& walkDirection) { - // do nothing -- walkVelocity is upated in preSimulation() - //_walkVelocity = walkDirection; -} - -void DynamicCharacterController::preStep(btCollisionWorld* collisionWorld) { +void MyAvatarController::preStep(btCollisionWorld* collisionWorld) { // trace a ray straight down to see if we're standing on the ground const btTransform& xform = _rigidBody->getWorldTransform(); @@ -96,7 +100,7 @@ void DynamicCharacterController::preStep(btCollisionWorld* collisionWorld) { } } -void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { +void MyAvatarController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { btVector3 actualVelocity = _rigidBody->getLinearVelocity(); btScalar actualSpeed = actualVelocity.length(); @@ -160,7 +164,7 @@ void DynamicCharacterController::playerStep(btCollisionWorld* dynaWorld, btScala } } -void DynamicCharacterController::jump() { +void MyAvatarController::jump() { // check for case where user is holding down "jump" key... // we'll eventually tansition to "hover" if (!_isJumping) { @@ -178,12 +182,12 @@ void DynamicCharacterController::jump() { } } -bool DynamicCharacterController::onGround() const { +bool MyAvatarController::onGround() const { const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; return _floorDistance < FLOOR_PROXIMITY_THRESHOLD; } -void DynamicCharacterController::setHovering(bool hover) { +void MyAvatarController::setHovering(bool hover) { if (hover != _isHovering) { _isHovering = hover; _isJumping = false; @@ -198,7 +202,7 @@ void DynamicCharacterController::setHovering(bool hover) { } } -void DynamicCharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { +void MyAvatarController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { _boxScale = scale; float x = _boxScale.x; @@ -231,15 +235,17 @@ void DynamicCharacterController::setLocalBoundingBox(const glm::vec3& corner, co _shapeLocalOffset = corner + 0.5f * _boxScale; } -bool DynamicCharacterController::needsRemoval() const { +/* moved to base class +bool MyAvatarController::needsRemoval() const { return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); } -bool DynamicCharacterController::needsAddition() const { +bool MyAvatarController::needsAddition() const { return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); } +*/ -void DynamicCharacterController::setEnabled(bool enabled) { +void MyAvatarController::setEnabled(bool enabled) { if (enabled != _enabled) { if (enabled) { // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. @@ -257,7 +263,8 @@ void DynamicCharacterController::setEnabled(bool enabled) { } } -void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { +/* moved to base class +void MyAvatarController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { if (_dynamicsWorld) { if (_rigidBody) { @@ -284,9 +291,9 @@ void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { } else { _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; } -} +}*/ -void DynamicCharacterController::updateShapeIfNecessary() { +void MyAvatarController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { // make sure there is NO pending removal from simulation at this point // (don't want to delete _rigidBody out from under the simulation) @@ -321,8 +328,8 @@ void DynamicCharacterController::updateShapeIfNecessary() { _rigidBody = new btRigidBody(mass, nullptr, _shape, inertia); _rigidBody->setSleepingThresholds(0.0f, 0.0f); _rigidBody->setAngularFactor(0.0f); - _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), - glmToBullet(_avatarData->getPosition()))); + _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatar->getOrientation()), + glmToBullet(_avatar->getPosition()))); if (_isHovering) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); } else { @@ -335,7 +342,7 @@ void DynamicCharacterController::updateShapeIfNecessary() { } } -void DynamicCharacterController::updateUpAxis(const glm::quat& rotation) { +void MyAvatarController::updateUpAxis(const glm::quat& rotation) { btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); if (!_isHovering) { @@ -346,24 +353,57 @@ void DynamicCharacterController::updateUpAxis(const glm::quat& rotation) { } } -void DynamicCharacterController::preSimulation(btScalar timeStep) { - if (_enabled && _dynamicsWorld) { - glm::quat rotation = _avatarData->getOrientation(); +void MyAvatarController::setAvatarPositionAndOrientation( + const glm::vec3& position, + const glm::quat& orientation) { + // TODO: update gravity if up has changed + updateUpAxis(orientation); + btQuaternion bodyOrientation = glmToBullet(orientation); + btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset); + _avatarBodyTransform = btTransform(bodyOrientation, bodyPosition); +} + +void MyAvatarController::getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { + if (_enabled && _rigidBody) { + const btTransform& avatarTransform = _rigidBody->getWorldTransform(); + rotation = bulletToGLM(avatarTransform.getRotation()); + position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset; + } +} + +void MyAvatarController::setTargetVelocity(const glm::vec3& velocity) { + //_walkVelocity = glmToBullet(_avatarData->getTargetVelocity()); + _walkVelocity = glmToBullet(velocity); +} + +void MyAvatarController::setHMDVelocity(const glm::vec3& velocity) { + _hmdVelocity = glmToBullet(velocity); +} + +glm::vec3 MyAvatarController::getLinearVelocity() const { + glm::vec3 velocity(0.0f); + if (_rigidBody) { + velocity = bulletToGLM(_rigidBody->getLinearVelocity()); + } + return velocity; +} + +void MyAvatarController::preSimulation() { + if (_enabled && _dynamicsWorld) { + /* + glm::quat rotation = _avatarData->getOrientation(); // TODO: update gravity if up has changed updateUpAxis(rotation); - glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; _rigidBody->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); + */ - // the rotation is dictated by AvatarData - btTransform xform = _rigidBody->getWorldTransform(); - xform.setRotation(glmToBullet(rotation)); - _rigidBody->setWorldTransform(xform); + _rigidBody->setWorldTransform(_avatarBodyTransform); // scan for distant floor // rayStart is at center of bottom sphere - btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp; + btVector3 rayStart = _avatarBodyTransform.getOrigin() - _halfHeight * _currentUp; // rayEnd is straight down MAX_FALL_HEIGHT btScalar rayLength = _radius + MAX_FALL_HEIGHT; @@ -388,8 +428,6 @@ void DynamicCharacterController::preSimulation(btScalar timeStep) { setHovering(true); } - _walkVelocity = glmToBullet(_avatarData->getTargetVelocity()); - if (_pendingFlags & PENDING_FLAG_JUMP) { _pendingFlags &= ~ PENDING_FLAG_JUMP; if (onGround()) { @@ -402,7 +440,9 @@ void DynamicCharacterController::preSimulation(btScalar timeStep) { } } -void DynamicCharacterController::postSimulation() { +void MyAvatarController::postSimulation() { + /* + _lastStepDuration += timeStep; if (_enabled && _rigidBody) { const btTransform& avatarTransform = _rigidBody->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); @@ -410,5 +450,8 @@ void DynamicCharacterController::postSimulation() { _avatarData->nextAttitude(position - rotation * _shapeLocalOffset, rotation); _avatarData->setVelocity(bulletToGLM(_rigidBody->getLinearVelocity())); + _avatarData->applySimulationTime(_lastStepDuration); } + _lastStepDuration = 0.0f; + */ } diff --git a/interface/src/avatar/MyAvatarController.h b/interface/src/avatar/MyAvatarController.h new file mode 100644 index 0000000000..a973905653 --- /dev/null +++ b/interface/src/avatar/MyAvatarController.h @@ -0,0 +1,106 @@ +// +// MyAvatar.h +// interface/src/avatar +// +// Created by AndrewMeadows 2015.10.21 +// 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_MyAvatarController_h +#define hifi_MyAvatarController_h + +#include +#include + +#include +#include + +class btCollisionShape; +class MyAvatar; + +class MyAvatarController : public CharacterController { +public: + MyAvatarController(MyAvatar* avatar); + ~MyAvatarController (); + + // TODO: implement these when needed + virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); } + virtual void reset(btCollisionWorld* collisionWorld) override { } + virtual void warp(const btVector3& origin) override { } + virtual void debugDraw(btIDebugDraw* debugDrawer) override { } + virtual void setUpInterpolate(bool value) override { } + + // overrides from btCharacterControllerInterface + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override { + preStep(collisionWorld); + playerStep(collisionWorld, deltaTime); + } + virtual void preStep(btCollisionWorld* collisionWorld) override; + virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt) override; + virtual bool canJump() const override { assert(false); return false; } // never call this + virtual void jump() override; // call this every frame the jump button is pressed + virtual bool onGround() const override; + + // overrides from CharacterController + virtual void preSimulation() override; + virtual void postSimulation() override; + virtual void incrementSimulationTime(btScalar timeStep) override { _lastStepDuration += timeStep; } + + bool isHovering() const { return _isHovering; } + void setHovering(bool enabled); + + void setEnabled(bool enabled); + bool isEnabled() const { return _enabled && _dynamicsWorld; } + + void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); + + virtual void updateShapeIfNecessary() override; + + void setAvatarPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation); + void getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; + + void setTargetVelocity(const glm::vec3& velocity); + void setHMDVelocity(const glm::vec3& velocity); + + glm::vec3 getLinearVelocity() const; + +protected: + void updateUpAxis(const glm::quat& rotation); + +protected: + btVector3 _currentUp; + btVector3 _walkVelocity; + btVector3 _hmdVelocity; + btTransform _avatarBodyTransform; + + glm::vec3 _shapeLocalOffset; + glm::vec3 _boxScale; // used to compute capsule shape + + quint64 _jumpToHoverStart; + + btCollisionShape* _shape { nullptr }; + MyAvatar* _avatar { nullptr }; + + btScalar _halfHeight; + btScalar _radius; + + btScalar _floorDistance; + + btScalar _gravity; + + btScalar _jumpSpeed; + btScalar _lastStepDuration; + + bool _enabled; + bool _isOnGround; + bool _isJumping; + bool _isFalling; + bool _isHovering; + bool _isPushingUp; +}; + +#endif // hifi_MyAvatarController_h diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp new file mode 100644 index 0000000000..9ba22cf2db --- /dev/null +++ b/libraries/physics/src/CharacterController.cpp @@ -0,0 +1,52 @@ +// +// CharacterControllerInterface.cpp +// libraries/physcis/src +// +// Created by Andrew Meadows 2015.10.21 +// 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 "CharacterController.h" + +#include "PhysicsCollisionGroups.h" + +bool CharacterController::needsRemoval() const { + return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); +} + +bool CharacterController::needsAddition() const { + return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); +} + +void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { + if (_dynamicsWorld != world) { + if (_dynamicsWorld) { + if (_rigidBody) { + _dynamicsWorld->removeRigidBody(_rigidBody); + _dynamicsWorld->removeAction(this); + } + _dynamicsWorld = nullptr; + } + if (world && _rigidBody) { + _dynamicsWorld = world; + _pendingFlags &= ~ PENDING_FLAG_JUMP; + _dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR); + _dynamicsWorld->addAction(this); + //reset(_dynamicsWorld); + } + } + if (_dynamicsWorld) { + if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { + // shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION; + } else { + _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; + } + } else { + _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; + } +} + diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h new file mode 100644 index 0000000000..cff257a790 --- /dev/null +++ b/libraries/physics/src/CharacterController.h @@ -0,0 +1,60 @@ +// +// CharacterControllerInterface.h +// libraries/physcis/src +// +// Created by Andrew Meadows 2015.10.21 +// 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_CharacterControllerInterface_h +#define hifi_CharacterControllerInterface_h + +#include +#include +#include +#include + +const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; +const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; +const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; +const uint32_t PENDING_FLAG_JUMP = 1U << 3; + + +class btRigidBody; +class btCollisionWorld; +class btDynamicsWorld; + +class CharacterController : public btCharacterControllerInterface { +public: + bool needsRemoval() const; + bool needsAddition() const; + void setDynamicsWorld(btDynamicsWorld* world); + btCollisionObject* getCollisionObject() { return _rigidBody; } + + virtual void updateShapeIfNecessary() = 0; + virtual void preSimulation() = 0; + virtual void incrementSimulationTime(btScalar stepTime) = 0; + virtual void postSimulation() = 0; + + virtual void setWalkDirection(const btVector3 &walkDirection) { assert(false); } + + /* these from btCharacterControllerInterface remain to be overridden + virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) = 0; + virtual void reset() = 0; + virtual void warp(const btVector3 &origin) = 0; + virtual void preStep(btCollisionWorld *collisionWorld) = 0; + virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) = 0; + virtual bool canJump() const = 0; + virtual void jump() = 0; + virtual bool onGround() const = 0; + */ +protected: + btDynamicsWorld* _dynamicsWorld { nullptr }; + btRigidBody* _rigidBody { nullptr }; + uint32_t _pendingFlags { 0 }; +}; + +#endif // hifi_CharacterControllerInterface_h diff --git a/libraries/physics/src/DynamicCharacterController.h b/libraries/physics/src/DynamicCharacterController.h deleted file mode 100644 index d26c69c5a5..0000000000 --- a/libraries/physics/src/DynamicCharacterController.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef hifi_DynamicCharacterController_h -#define hifi_DynamicCharacterController_h - -#include -#include - -#include - -class btCollisionShape; -class btRigidBody; -class btCollisionWorld; - -const int NUM_CHARACTER_CONTROLLER_RAYS = 2; - -class DynamicCharacterController : public btCharacterControllerInterface -{ -protected: - btScalar _halfHeight; - btScalar _radius; - btCollisionShape* _shape; - btRigidBody* _rigidBody; - - btVector3 _currentUp; - - btScalar _floorDistance; - - btVector3 _walkVelocity; - btScalar _gravity; - - glm::vec3 _shapeLocalOffset; - glm::vec3 _boxScale; // used to compute capsule shape - AvatarData* _avatarData = nullptr; - - bool _enabled; - bool _isOnGround; - bool _isJumping; - bool _isFalling; - bool _isHovering; - bool _isPushingUp; - quint64 _jumpToHoverStart; - uint32_t _pendingFlags; - - btDynamicsWorld* _dynamicsWorld = nullptr; - - btScalar _jumpSpeed; - -public: - DynamicCharacterController(AvatarData* avatarData); - ~DynamicCharacterController (); - - virtual void setWalkDirection(const btVector3& walkDirection); - virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) { assert(false); } - - // TODO: implement these when needed - virtual void reset(btCollisionWorld* collisionWorld) { } - virtual void warp(const btVector3& origin) { } - virtual void debugDraw(btIDebugDraw* debugDrawer) { } - virtual void setUpInterpolate(bool value) { } - - btCollisionObject* getCollisionObject() { return _rigidBody; } - - ///btActionInterface interface - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) { - preStep(collisionWorld); - playerStep(collisionWorld, deltaTime); - } - - virtual void preStep(btCollisionWorld* collisionWorld); - virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt); - - virtual bool canJump() const { assert(false); return false; } // never call this - virtual void jump(); // call this every frame the jump button is pressed - virtual bool onGround() const; - bool isHovering() const { return _isHovering; } - void setHovering(bool enabled); - - bool needsRemoval() const; - bool needsAddition() const; - void setEnabled(bool enabled); - bool isEnabled() const { return _enabled && _dynamicsWorld; } - - void setDynamicsWorld(btDynamicsWorld* world); - - void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); - bool needsShapeUpdate() const; - void updateShapeIfNecessary(); - - void preSimulation(btScalar timeStep); - void postSimulation(); - -protected: - void updateUpAxis(const glm::quat& rotation); -}; - -#endif // hifi_DynamicCharacterController_h diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 75273e62ba..a6b8dbe959 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -11,6 +11,7 @@ #include +#include "CharacterController.h" #include "ObjectMotionState.h" #include "PhysicsEngine.h" #include "PhysicsHelpers.h" @@ -23,7 +24,7 @@ uint32_t PhysicsEngine::getNumSubsteps() { PhysicsEngine::PhysicsEngine(const glm::vec3& offset) : _originOffset(offset), - _characterController(nullptr) { + _myAvatarController(nullptr) { // build table of masks with their group as the key _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_DEFAULT), COLLISION_MASK_DEFAULT); _collisionMasks.insert(btHashInt((int)COLLISION_GROUP_STATIC), COLLISION_MASK_STATIC); @@ -38,8 +39,8 @@ PhysicsEngine::PhysicsEngine(const glm::vec3& offset) : } PhysicsEngine::~PhysicsEngine() { - if (_characterController) { - _characterController->setDynamicsWorld(nullptr); + if (_myAvatarController) { + _myAvatarController->setDynamicsWorld(nullptr); } delete _collisionConfig; delete _collisionDispatcher; @@ -239,16 +240,18 @@ void PhysicsEngine::stepSimulation() { _clock.reset(); float timeStep = btMin(dt, MAX_TIMESTEP); - // TODO: move character->preSimulation() into relayIncomingChanges - if (_characterController) { - if (_characterController->needsRemoval()) { - _characterController->setDynamicsWorld(nullptr); + if (_myAvatarController) { + // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because + // the updateShapeIfNecessary() call needs info from MyAvatar and should + // be done on the main thread during the pre-simulation stuff + if (_myAvatarController->needsRemoval()) { + _myAvatarController->setDynamicsWorld(nullptr); } - _characterController->updateShapeIfNecessary(); - if (_characterController->needsAddition()) { - _characterController->setDynamicsWorld(_dynamicsWorld); + _myAvatarController->updateShapeIfNecessary(); + if (_myAvatarController->needsAddition()) { + _myAvatarController->setDynamicsWorld(_dynamicsWorld); } - _characterController->preSimulation(timeStep); + _myAvatarController->preSimulation(); } int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); @@ -257,8 +260,8 @@ void PhysicsEngine::stepSimulation() { _numSubsteps += (uint32_t)numSubsteps; ObjectMotionState::setWorldSimulationStep(_numSubsteps); - if (_characterController) { - _characterController->postSimulation(); + if (_myAvatarController) { + _myAvatarController->postSimulation(); } updateContactMap(); _hasOutgoingChanges = true; @@ -268,7 +271,7 @@ void PhysicsEngine::stepSimulation() { void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) { BT_PROFILE("ownershipInfection"); - const btCollisionObject* characterObject = _characterController ? _characterController->getCollisionObject() : nullptr; + const btCollisionObject* characterObject = _myAvatarController ? _myAvatarController->getCollisionObject() : nullptr; ObjectMotionState* motionStateA = static_cast(objectA->getUserPointer()); ObjectMotionState* motionStateB = static_cast(objectB->getUserPointer()); @@ -431,15 +434,15 @@ void PhysicsEngine::bump(ObjectMotionState* motionState) { } } -void PhysicsEngine::setCharacterController(DynamicCharacterController* character) { - if (_characterController != character) { - if (_characterController) { +void PhysicsEngine::setCharacterController(CharacterController* character) { + if (_myAvatarController != character) { + if (_myAvatarController) { // remove the character from the DynamicsWorld immediately - _characterController->setDynamicsWorld(nullptr); - _characterController = nullptr; + _myAvatarController->setDynamicsWorld(nullptr); + _myAvatarController = nullptr; } // the character will be added to the DynamicsWorld later - _characterController = character; + _myAvatarController = character; } } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 0a317652da..e7b5fd79d4 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -21,13 +21,14 @@ #include "BulletUtil.h" #include "ContactInfo.h" -#include "DynamicCharacterController.h" #include "ObjectMotionState.h" #include "ThreadSafeDynamicsWorld.h" #include "ObjectAction.h" const float HALF_SIMULATION_EXTENT = 512.0f; // meters +class CharacterController; + // simple class for keeping track of contacts class ContactKey { public: @@ -87,7 +88,7 @@ public: void removeRigidBody(btRigidBody* body); - void setCharacterController(DynamicCharacterController* character); + void setCharacterController(CharacterController* character); void dumpNextStats() { _dumpNextStats = true; } @@ -117,7 +118,7 @@ private: uint32_t _lastNumSubstepsAtUpdateInternal = 0; /// character collisions - DynamicCharacterController* _characterController = NULL; + CharacterController* _myAvatarController; bool _dumpNextStats = false; bool _hasOutgoingChanges = false; From 5f278f90246004dc0bbafe69d3e2082766bae4c6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Oct 2015 09:44:22 -0700 Subject: [PATCH 0450/1003] name change --- interface/src/avatar/MyAvatar.h | 6 +-- ...ntroller.cpp => MyCharacterController.cpp} | 44 +++++++++---------- ...arController.h => MyCharacterController.h} | 14 +++--- 3 files changed, 32 insertions(+), 32 deletions(-) rename interface/src/avatar/{MyAvatarController.cpp => MyCharacterController.cpp} (91%) rename interface/src/avatar/{MyAvatarController.h => MyCharacterController.h} (91%) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f3f55f4a7a..2f2b8ca3b5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -19,7 +19,7 @@ #include "Avatar.h" //#include "AtRestDetector.h" -#include "MyAvatarController.h" +#include "MyCharacterController.h" //#define OLD_HMD_TRACKER @@ -158,7 +158,7 @@ public: virtual void setAttachmentData(const QVector& attachmentData) override; - MyAvatarController* getCharacterController() { return &_characterController; } + MyCharacterController* getCharacterController() { return &_characterController; } void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(); @@ -305,7 +305,7 @@ private: quint32 _motionBehaviors; QString _collisionSoundURL; - MyAvatarController _characterController; + MyCharacterController _characterController; AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; diff --git a/interface/src/avatar/MyAvatarController.cpp b/interface/src/avatar/MyCharacterController.cpp similarity index 91% rename from interface/src/avatar/MyAvatarController.cpp rename to interface/src/avatar/MyCharacterController.cpp index 931c8b9b08..05d6c57bb8 100644 --- a/interface/src/avatar/MyAvatarController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "MyAvatarController.h" +#include "MyCharacterController.h" #include #include @@ -50,7 +50,7 @@ protected: btRigidBody* _me; }; -MyAvatarController::MyAvatarController(MyAvatar* avatar) { +MyCharacterController::MyCharacterController(MyAvatar* avatar) { _halfHeight = 1.0f; _shape = nullptr; @@ -76,10 +76,10 @@ MyAvatarController::MyAvatarController(MyAvatar* avatar) { updateShapeIfNecessary(); } -MyAvatarController::~MyAvatarController() { +MyCharacterController::~MyCharacterController() { } -void MyAvatarController::preStep(btCollisionWorld* collisionWorld) { +void MyCharacterController::preStep(btCollisionWorld* collisionWorld) { // trace a ray straight down to see if we're standing on the ground const btTransform& xform = _rigidBody->getWorldTransform(); @@ -100,7 +100,7 @@ void MyAvatarController::preStep(btCollisionWorld* collisionWorld) { } } -void MyAvatarController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { +void MyCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { btVector3 actualVelocity = _rigidBody->getLinearVelocity(); btScalar actualSpeed = actualVelocity.length(); @@ -164,7 +164,7 @@ void MyAvatarController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { } } -void MyAvatarController::jump() { +void MyCharacterController::jump() { // check for case where user is holding down "jump" key... // we'll eventually tansition to "hover" if (!_isJumping) { @@ -182,12 +182,12 @@ void MyAvatarController::jump() { } } -bool MyAvatarController::onGround() const { +bool MyCharacterController::onGround() const { const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; return _floorDistance < FLOOR_PROXIMITY_THRESHOLD; } -void MyAvatarController::setHovering(bool hover) { +void MyCharacterController::setHovering(bool hover) { if (hover != _isHovering) { _isHovering = hover; _isJumping = false; @@ -202,7 +202,7 @@ void MyAvatarController::setHovering(bool hover) { } } -void MyAvatarController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { +void MyCharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { _boxScale = scale; float x = _boxScale.x; @@ -236,16 +236,16 @@ void MyAvatarController::setLocalBoundingBox(const glm::vec3& corner, const glm: } /* moved to base class -bool MyAvatarController::needsRemoval() const { +bool MyCharacterController::needsRemoval() const { return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); } -bool MyAvatarController::needsAddition() const { +bool MyCharacterController::needsAddition() const { return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); } */ -void MyAvatarController::setEnabled(bool enabled) { +void MyCharacterController::setEnabled(bool enabled) { if (enabled != _enabled) { if (enabled) { // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. @@ -264,7 +264,7 @@ void MyAvatarController::setEnabled(bool enabled) { } /* moved to base class -void MyAvatarController::setDynamicsWorld(btDynamicsWorld* world) { +void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { if (_dynamicsWorld) { if (_rigidBody) { @@ -293,7 +293,7 @@ void MyAvatarController::setDynamicsWorld(btDynamicsWorld* world) { } }*/ -void MyAvatarController::updateShapeIfNecessary() { +void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { // make sure there is NO pending removal from simulation at this point // (don't want to delete _rigidBody out from under the simulation) @@ -342,7 +342,7 @@ void MyAvatarController::updateShapeIfNecessary() { } } -void MyAvatarController::updateUpAxis(const glm::quat& rotation) { +void MyCharacterController::updateUpAxis(const glm::quat& rotation) { btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); if (!_isHovering) { @@ -353,7 +353,7 @@ void MyAvatarController::updateUpAxis(const glm::quat& rotation) { } } -void MyAvatarController::setAvatarPositionAndOrientation( +void MyCharacterController::setAvatarPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation) { // TODO: update gravity if up has changed @@ -364,7 +364,7 @@ void MyAvatarController::setAvatarPositionAndOrientation( _avatarBodyTransform = btTransform(bodyOrientation, bodyPosition); } -void MyAvatarController::getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { +void MyCharacterController::getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { if (_enabled && _rigidBody) { const btTransform& avatarTransform = _rigidBody->getWorldTransform(); rotation = bulletToGLM(avatarTransform.getRotation()); @@ -372,16 +372,16 @@ void MyAvatarController::getAvatarPositionAndOrientation(glm::vec3& position, gl } } -void MyAvatarController::setTargetVelocity(const glm::vec3& velocity) { +void MyCharacterController::setTargetVelocity(const glm::vec3& velocity) { //_walkVelocity = glmToBullet(_avatarData->getTargetVelocity()); _walkVelocity = glmToBullet(velocity); } -void MyAvatarController::setHMDVelocity(const glm::vec3& velocity) { +void MyCharacterController::setHMDVelocity(const glm::vec3& velocity) { _hmdVelocity = glmToBullet(velocity); } -glm::vec3 MyAvatarController::getLinearVelocity() const { +glm::vec3 MyCharacterController::getLinearVelocity() const { glm::vec3 velocity(0.0f); if (_rigidBody) { velocity = bulletToGLM(_rigidBody->getLinearVelocity()); @@ -389,7 +389,7 @@ glm::vec3 MyAvatarController::getLinearVelocity() const { return velocity; } -void MyAvatarController::preSimulation() { +void MyCharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { /* glm::quat rotation = _avatarData->getOrientation(); @@ -440,7 +440,7 @@ void MyAvatarController::preSimulation() { } } -void MyAvatarController::postSimulation() { +void MyCharacterController::postSimulation() { /* _lastStepDuration += timeStep; if (_enabled && _rigidBody) { diff --git a/interface/src/avatar/MyAvatarController.h b/interface/src/avatar/MyCharacterController.h similarity index 91% rename from interface/src/avatar/MyAvatarController.h rename to interface/src/avatar/MyCharacterController.h index a973905653..2533ec2680 100644 --- a/interface/src/avatar/MyAvatarController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -1,5 +1,5 @@ // -// MyAvatar.h +// MyCharacterController.h // interface/src/avatar // // Created by AndrewMeadows 2015.10.21 @@ -10,8 +10,8 @@ // -#ifndef hifi_MyAvatarController_h -#define hifi_MyAvatarController_h +#ifndef hifi_MyCharacterController_h +#define hifi_MyCharacterController_h #include #include @@ -22,10 +22,10 @@ class btCollisionShape; class MyAvatar; -class MyAvatarController : public CharacterController { +class MyCharacterController : public CharacterController { public: - MyAvatarController(MyAvatar* avatar); - ~MyAvatarController (); + MyCharacterController(MyAvatar* avatar); + ~MyCharacterController (); // TODO: implement these when needed virtual void setVelocityForTimeInterval(const btVector3 &velocity, btScalar timeInterval) override { assert(false); } @@ -103,4 +103,4 @@ protected: bool _isPushingUp; }; -#endif // hifi_MyAvatarController_h +#endif // hifi_MyCharacterController_h From 5e59e9595b1f2bded6021189ca44355acf0f6fb5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Oct 2015 09:45:47 -0700 Subject: [PATCH 0451/1003] fix typo in comments --- interface/src/avatar/MyCharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 05d6c57bb8..4c77384e88 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -1,5 +1,5 @@ // -// MyAvatar.h +// MyCharacterController.h // interface/src/avatar // // Created by AndrewMeadows 2015.10.21 From 355ebe923dd94b879cf9cbc0c1400768fe1eb551 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Oct 2015 09:51:36 -0700 Subject: [PATCH 0452/1003] cleanup and comments --- .../src/avatar/MyCharacterController.cpp | 63 +------------------ 1 file changed, 2 insertions(+), 61 deletions(-) diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 4c77384e88..0ae4b240a2 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -235,16 +235,6 @@ void MyCharacterController::setLocalBoundingBox(const glm::vec3& corner, const g _shapeLocalOffset = corner + 0.5f * _boxScale; } -/* moved to base class -bool MyCharacterController::needsRemoval() const { - return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); -} - -bool MyCharacterController::needsAddition() const { - return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); -} -*/ - void MyCharacterController::setEnabled(bool enabled) { if (enabled != _enabled) { if (enabled) { @@ -263,36 +253,6 @@ void MyCharacterController::setEnabled(bool enabled) { } } -/* moved to base class -void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) { - if (_dynamicsWorld != world) { - if (_dynamicsWorld) { - if (_rigidBody) { - _dynamicsWorld->removeRigidBody(_rigidBody); - _dynamicsWorld->removeAction(this); - } - _dynamicsWorld = nullptr; - } - if (world && _rigidBody) { - _dynamicsWorld = world; - _pendingFlags &= ~ PENDING_FLAG_JUMP; - _dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR); - _dynamicsWorld->addAction(this); - //reset(_dynamicsWorld); - } - } - if (_dynamicsWorld) { - if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { - // shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION; - } else { - _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; - } - } else { - _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; - } -}*/ - void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { // make sure there is NO pending removal from simulation at this point @@ -391,14 +351,7 @@ glm::vec3 MyCharacterController::getLinearVelocity() const { void MyCharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { - /* - glm::quat rotation = _avatarData->getOrientation(); - // TODO: update gravity if up has changed - updateUpAxis(rotation); - glm::vec3 position = _avatarData->getPosition() + rotation * _shapeLocalOffset; - _rigidBody->setWorldTransform(btTransform(glmToBullet(rotation), glmToBullet(position))); - */ - + // slam body to where it is supposed to be _rigidBody->setWorldTransform(_avatarBodyTransform); // scan for distant floor @@ -441,17 +394,5 @@ void MyCharacterController::preSimulation() { } void MyCharacterController::postSimulation() { - /* - _lastStepDuration += timeStep; - if (_enabled && _rigidBody) { - const btTransform& avatarTransform = _rigidBody->getWorldTransform(); - glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); - glm::vec3 position = bulletToGLM(avatarTransform.getOrigin()); - - _avatarData->nextAttitude(position - rotation * _shapeLocalOffset, rotation); - _avatarData->setVelocity(bulletToGLM(_rigidBody->getLinearVelocity())); - _avatarData->applySimulationTime(_lastStepDuration); - } - _lastStepDuration = 0.0f; - */ + // postSimulation() exists for symmetry and just in case we need to do something here later } From 24c2f538a42bb34780b5a9310e44fdf59c504b35 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Oct 2015 10:52:49 -0700 Subject: [PATCH 0453/1003] keep MyAvatar RigidBody on shape changes --- .../src/avatar/MyCharacterController.cpp | 27 +++++++++---------- interface/src/avatar/MyCharacterController.h | 1 - 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 0ae4b240a2..3e9755c6ca 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -52,7 +52,6 @@ protected: MyCharacterController::MyCharacterController(MyAvatar* avatar) { _halfHeight = 1.0f; - _shape = nullptr; assert(avatar); _avatar = avatar; @@ -255,15 +254,7 @@ void MyCharacterController::setEnabled(bool enabled) { void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { - // make sure there is NO pending removal from simulation at this point - // (don't want to delete _rigidBody out from under the simulation) - assert(!(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION)); _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; - // delete shape and RigidBody - delete _rigidBody; - _rigidBody = nullptr; - delete _shape; - _shape = nullptr; // compute new dimensions from avatar's bounding box float x = _boxScale.x; @@ -277,15 +268,23 @@ void MyCharacterController::updateShapeIfNecessary() { // NOTE: _shapeLocalOffset is already computed if (_radius > 0.0f) { - // create new shape - _shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); - // HACK: use some simple mass property defaults for now float mass = 100.0f; btVector3 inertia(30.0f, 8.0f, 30.0f); - // create new body - _rigidBody = new btRigidBody(mass, nullptr, _shape, inertia); + // create RigidBody if it doesn't exist + if (!_rigidBody) { + btCollisionShape* shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); + _rigidBody = new btRigidBody(mass, nullptr, shape, inertia); + } else { + btCollisionShape* shape = _rigidBody->getCollisionShape(); + if (shape) { + delete shape; + } + shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); + _rigidBody->setCollisionShape(shape); + } + _rigidBody->setSleepingThresholds(0.0f, 0.0f); _rigidBody->setAngularFactor(0.0f); _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatar->getOrientation()), diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index 2533ec2680..5ee9f50f7e 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -82,7 +82,6 @@ protected: quint64 _jumpToHoverStart; - btCollisionShape* _shape { nullptr }; MyAvatar* _avatar { nullptr }; btScalar _halfHeight; From 43aac813daab023eeb5003ed3d0db072f104cf7d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 22 Oct 2015 11:46:55 -0700 Subject: [PATCH 0454/1003] more prep for shifting avatar during HMD motion --- interface/src/avatar/MyAvatar.cpp | 17 +++++++++++------ interface/src/avatar/MyAvatar.h | 5 ++--- interface/src/avatar/MyCharacterController.cpp | 13 ++++++++++++- interface/src/avatar/MyCharacterController.h | 3 ++- libraries/physics/src/CharacterController.h | 1 - 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e52e56cd89..cfcd7a69f9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -114,7 +114,6 @@ MyAvatar::MyAvatar(RigPointer rig) : ,_hmdAtRestDetector(glm::vec3(0), glm::quat()) #else ,_avatarOffsetFromHMD(0.0f) - ,_hmdVelocity(0.0f) #endif // OLD_HMD_TRACKER { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { @@ -375,9 +374,16 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } followHMD(deltaTime); +#else + // TODO adebug BOOKMARK -- this is where we need to add the new code for HMD_TRACKER #endif // OLD_HMD_TRACKER } +glm::vec3 MyAvatar::getHMDCorrectionVelocity() const { + // TODO: impelement this + return Vectors::ZERO; +} + #ifdef OLD_HMD_TRACKER void MyAvatar::beginFollowingHMD() { // begin homing toward derived body position. @@ -433,10 +439,6 @@ void MyAvatar::followHMD(float deltaTime) { _bodySensorMatrix = createMatFromQuatAndPos(rot, pos); } } -} -#else -void MyAvatar::harvestHMDOffset(glm::vec3 offset) { - } #endif // USE_OLD @@ -1292,7 +1294,7 @@ void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); _characterController.setTargetVelocity(getTargetVelocity()); _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); - //_characterController.setHMDVelocity(hmdVelocity); + _characterController.setHMDVelocity(getHMDCorrectionVelocity()); } void MyAvatar::harvestResultsFromPhysicsSimulation() { @@ -1301,6 +1303,9 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() { _characterController.getAvatarPositionAndOrientation(position, orientation); nextAttitude(position, orientation); setVelocity(_characterController.getLinearVelocity()); + + // adebug TODO: harvest HMD shift here + //glm::vec3 hmdShift = _characterController.getHMDShift(); } QString MyAvatar::getScriptedMotorFrame() const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2f2b8ca3b5..36c73d672d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -76,6 +76,8 @@ public: // as it moves through the world. void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); + glm::vec3 getHMDCorrectionVelocity() const; + // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. @@ -272,8 +274,6 @@ private: void beginFollowingHMD(); bool shouldFollowHMD() const; void followHMD(float deltaTime); -#else - void harvestHMDOffset(glm::vec3 offset); #endif bool cameraInsideHead() const; @@ -376,7 +376,6 @@ private: bool _lastIsMoving { false }; #else glm::vec3 _avatarOffsetFromHMD; - glm::vec3 _hmdVelocity; #endif // OLD_HMD_TRACKER }; diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index 3e9755c6ca..ad2ca32b05 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -161,6 +160,17 @@ void MyCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) _rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection); } } + + // Rather than add _hmdVelocity to the velocity of the RigidBody, we explicitly teleport + // the RigidBody forward according to the formula: distance = rate * time + if (_hmdVelocity.length2() > 0.0f) { + btTransform bodyTransform = _rigidBody->getWorldTransform(); + bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _hmdVelocity); + _rigidBody->setWorldTransform(bodyTransform); + } + // MyAvatar will ask us how far we stepped for HMD motion, which will depend on how + // much time has accumulated in _lastStepDuration. + _lastStepDuration += dt; } void MyCharacterController::jump() { @@ -390,6 +400,7 @@ void MyCharacterController::preSimulation() { } } } + _lastStepDuration = 0.0f; } void MyCharacterController::postSimulation() { diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index 5ee9f50f7e..de711c84f4 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -48,7 +49,6 @@ public: // overrides from CharacterController virtual void preSimulation() override; virtual void postSimulation() override; - virtual void incrementSimulationTime(btScalar timeStep) override { _lastStepDuration += timeStep; } bool isHovering() const { return _isHovering; } void setHovering(bool enabled); @@ -65,6 +65,7 @@ public: void setTargetVelocity(const glm::vec3& velocity); void setHMDVelocity(const glm::vec3& velocity); + glm::vec3 getHMDShift() const { return _lastStepDuration * bulletToGLM(_hmdVelocity); } glm::vec3 getLinearVelocity() const; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index cff257a790..e9e6f1328e 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -36,7 +36,6 @@ public: virtual void updateShapeIfNecessary() = 0; virtual void preSimulation() = 0; - virtual void incrementSimulationTime(btScalar stepTime) = 0; virtual void postSimulation() = 0; virtual void setWalkDirection(const btVector3 &walkDirection) { assert(false); } From ed6a866ff9dddc7b6cf1aee39bdaf9b3f52e1eda Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 13:43:13 -0700 Subject: [PATCH 0455/1003] ready for demo --- examples/painting/whiteboard/whiteboardEntityScript.js | 2 +- examples/painting/whiteboard/whiteboardSpawner.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index 2d34fc8c40..f38073f389 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -239,7 +239,7 @@ unload: function() { Overlays.deleteOverlay(this.laserPointer); - this.eraseBoard(); + // this.eraseBoard(); } }; diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 16953129af..82d7f63239 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -17,7 +17,7 @@ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); //var modelURL = "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx"; -//var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); +// var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx"; var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); @@ -192,4 +192,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -Script.scriptEnding.connect(cleanup); \ No newline at end of file +// Script.scriptEnding.connect(cleanup); \ No newline at end of file From d67f4958cdab2d60d2b7e08654e5b62667fbcb20 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 13:44:29 -0700 Subject: [PATCH 0456/1003] wait longer for dimensions --- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 82d7f63239..4303803266 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -65,7 +65,7 @@ var drawingSurface = Entities.addEntity({ Script.setTimeout(function() { whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions; setUp(); -}, 500) +}, 1000) function setUp() { From 8b285fd2287a3a6a02567208303f6f3e34693580 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 27 Oct 2015 13:51:40 -0700 Subject: [PATCH 0457/1003] recover follow HMD behavior --- interface/src/avatar/MyAvatar.cpp | 18 ++---------------- interface/src/avatar/MyAvatar.h | 9 +-------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cfcd7a69f9..acde1dc599 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -109,12 +109,8 @@ MyAvatar::MyAvatar(RigPointer rig) : _goToOrientation(), _rig(rig), _prevShouldDrawHead(true), - _audioListenerMode(FROM_HEAD) -#ifdef OLD_HMD_TRACKER - ,_hmdAtRestDetector(glm::vec3(0), glm::quat()) -#else - ,_avatarOffsetFromHMD(0.0f) -#endif // OLD_HMD_TRACKER + _audioListenerMode(FROM_HEAD), + _hmdAtRestDetector(glm::vec3(0), glm::quat()) { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -159,9 +155,7 @@ void MyAvatar::reset(bool andReload) { // Reset dynamic state. _wasPushing = _isPushing = _isBraking = _billboardValid = false; -#ifdef OLD_HMD_TRACKER _isFollowingHMD = false; -#endif // OLD_HMD_TRACKER _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -309,7 +303,6 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrix; } -#ifdef OLD_HMD_TRACKER // returns true if pos is OUTSIDE of the vertical capsule // where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad. static bool pointIsOutsideCapsule(const glm::vec3& pos, float capsuleLen, float capsuleRad) { @@ -328,7 +321,6 @@ static bool pointIsOutsideCapsule(const glm::vec3& pos, float capsuleLen, float return false; } } -#endif // OLD_HMD_TRACKER // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD @@ -339,7 +331,6 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorPosition = extractTranslation(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); -#ifdef OLD_HMD_TRACKER // calc deltaTime auto now = usecTimestampNow(); auto deltaUsecs = now - _lastUpdateFromHMDTime; @@ -374,9 +365,6 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } followHMD(deltaTime); -#else - // TODO adebug BOOKMARK -- this is where we need to add the new code for HMD_TRACKER -#endif // OLD_HMD_TRACKER } glm::vec3 MyAvatar::getHMDCorrectionVelocity() const { @@ -384,7 +372,6 @@ glm::vec3 MyAvatar::getHMDCorrectionVelocity() const { return Vectors::ZERO; } -#ifdef OLD_HMD_TRACKER void MyAvatar::beginFollowingHMD() { // begin homing toward derived body position. if (!_isFollowingHMD) { @@ -440,7 +427,6 @@ void MyAvatar::followHMD(float deltaTime) { } } } -#endif // USE_OLD // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 36c73d672d..9e66472328 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -18,10 +18,9 @@ #include #include "Avatar.h" -//#include "AtRestDetector.h" +#include "AtRestDetector.h" #include "MyCharacterController.h" -//#define OLD_HMD_TRACKER class ModelItemID; @@ -270,11 +269,9 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } -#ifdef OLD_HMD_TRACKER void beginFollowingHMD(); bool shouldFollowHMD() const; void followHMD(float deltaTime); -#endif bool cameraInsideHead() const; @@ -366,7 +363,6 @@ private: glm::vec3 _customListenPosition; glm::quat _customListenOrientation; -#ifdef OLD_HMD_TRACKER bool _isFollowingHMD { false }; float _followHMDAlpha{0.0f}; @@ -374,9 +370,6 @@ private: AtRestDetector _hmdAtRestDetector; glm::vec3 _lastPosition; bool _lastIsMoving { false }; -#else - glm::vec3 _avatarOffsetFromHMD; -#endif // OLD_HMD_TRACKER }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); From 5a42991e0ccd075100c2da985f7d67925fd076c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 12:30:34 -0700 Subject: [PATCH 0458/1003] 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 32f88f3fb6a664d5e664db51415fd8ecc0dd007d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 Oct 2015 14:37:39 -0700 Subject: [PATCH 0459/1003] Fix for JSConcole --- interface/src/ui/JSConsole.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index b50f84d538..2d34ba5608 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -106,8 +106,11 @@ void JSConsole::executeCommand(const QString& command) { QScriptValue JSConsole::executeCommandInWatcher(const QString& command) { QScriptValue result; + static const QString filename = "JSConcole"; QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection, - Q_RETURN_ARG(QScriptValue, result), Q_ARG(const QString&, command)); + Q_RETURN_ARG(QScriptValue, result), + Q_ARG(const QString&, command), + Q_ARG(const QString&, filename)); return result; } From e35596428c7e9445036131326122a5efc3d59cd4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 27 Oct 2015 14:43:18 -0700 Subject: [PATCH 0460/1003] cleanup comment --- interface/src/avatar/MyAvatar.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index acde1dc599..f7fad2bd2b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1289,8 +1289,7 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() { _characterController.getAvatarPositionAndOrientation(position, orientation); nextAttitude(position, orientation); setVelocity(_characterController.getLinearVelocity()); - - // adebug TODO: harvest HMD shift here + // TODO: harvest HMD shift here //glm::vec3 hmdShift = _characterController.getHMDShift(); } From c9cd3364005f29368c5412560adb86fea0fc381d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 27 Oct 2015 15:01:48 -0700 Subject: [PATCH 0461/1003] fix for bad contacts after changing avatar models --- libraries/physics/src/PhysicsEngine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index a6b8dbe959..d6772f8d36 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -246,6 +246,11 @@ void PhysicsEngine::stepSimulation() { // be done on the main thread during the pre-simulation stuff if (_myAvatarController->needsRemoval()) { _myAvatarController->setDynamicsWorld(nullptr); + + // We must remove any existing contacts for the avatar so that any new contacts will have + // valid data. MyAvatar's RigidBody is the ONLY one in the simulation that does not yet + // have a MotionState so we pass nullptr to removeContacts(). + removeContacts(nullptr); } _myAvatarController->updateShapeIfNecessary(); if (_myAvatarController->needsAddition()) { From 5cbb2562cd1b72ea77d0f60703d34a0f0365a241 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 27 Oct 2015 11:04:50 -0700 Subject: [PATCH 0462/1003] Move GL utilities out to their own library --- interface/CMakeLists.txt | 3 +- interface/src/Application.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- libraries/display-plugins/CMakeLists.txt | 2 +- .../src/display-plugins/DisplayPlugin.h | 4 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 2 +- .../src/display-plugins/OpenGLDisplayPlugin.h | 6 +-- .../oculus/OculusLegacyDisplayPlugin.cpp | 4 +- .../openvr/OpenVrDisplayPlugin.cpp | 2 +- .../stereo/InterleavedStereoDisplayPlugin.cpp | 2 +- .../stereo/SideBySideStereoDisplayPlugin.cpp | 2 +- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/RenderablePolyLineEntityItem.cpp | 2 +- .../src/RenderablePolyLineEntityItem.h | 3 +- .../src/RenderableWebEntityItem.cpp | 2 +- libraries/gl/CMakeLists.txt | 7 ++++ .../gpu/GPUConfig.h => gl/src/gl/Config.h} | 0 .../src => gl/src/gl}/GLEscrow.h | 0 .../{shared/src => gl/src/gl}/GLHelpers.cpp | 0 .../{shared/src => gl/src/gl}/GLHelpers.h | 0 .../src => gl/src/gl}/GlWindow.cpp | 7 ++-- .../src => gl/src/gl}/GlWindow.h | 0 .../src => gl/src/gl}/OffscreenGlCanvas.cpp | 9 +++-- .../src => gl/src/gl}/OffscreenGlCanvas.h | 0 .../src => gl/src/gl}/OffscreenQmlSurface.cpp | 5 +-- .../src => gl/src/gl}/OffscreenQmlSurface.h | 0 .../src => gl/src/gl}/OglplusHelpers.cpp | 0 .../src => gl/src/gl}/OglplusHelpers.h | 0 libraries/gpu/CMakeLists.txt | 2 +- libraries/gpu/src/gpu/GLBackend.h | 2 +- libraries/render-utils/src/GeometryCache.cpp | 40 +++++++++---------- libraries/ui/CMakeLists.txt | 2 +- libraries/ui/src/OffscreenUi.h | 2 +- tests/gpu-test/CMakeLists.txt | 2 +- tests/render-utils/CMakeLists.txt | 2 +- tests/shaders/CMakeLists.txt | 4 +- tests/shaders/src/main.cpp | 2 +- tests/ui/CMakeLists.txt | 2 +- 38 files changed, 66 insertions(+), 62 deletions(-) create mode 100644 libraries/gl/CMakeLists.txt rename libraries/{gpu/src/gpu/GPUConfig.h => gl/src/gl/Config.h} (100%) rename libraries/{render-utils/src => gl/src/gl}/GLEscrow.h (100%) rename libraries/{shared/src => gl/src/gl}/GLHelpers.cpp (100%) rename libraries/{shared/src => gl/src/gl}/GLHelpers.h (100%) rename libraries/{render-utils/src => gl/src/gl}/GlWindow.cpp (94%) rename libraries/{render-utils/src => gl/src/gl}/GlWindow.h (100%) rename libraries/{render-utils/src => gl/src/gl}/OffscreenGlCanvas.cpp (94%) rename libraries/{render-utils/src => gl/src/gl}/OffscreenGlCanvas.h (100%) rename libraries/{render-utils/src => gl/src/gl}/OffscreenQmlSurface.cpp (99%) rename libraries/{render-utils/src => gl/src/gl}/OffscreenQmlSurface.h (100%) rename libraries/{render-utils/src => gl/src/gl}/OglplusHelpers.cpp (100%) rename libraries/{render-utils/src => gl/src/gl}/OglplusHelpers.h (100%) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 00f6d2ecea..f9c92c59e7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -96,7 +96,8 @@ else() endif() # link required hifi libraries -link_hifi_libraries(shared octree environment gpu procedural model render fbx networking model-networking entities avatars +link_hifi_libraries(shared octree environment gpu gl 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) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1b6be53876..b4d43ea1d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -70,7 +70,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 0f2361410b..1088473a25 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 504370796a..14aa03de44 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME display-plugins) setup_hifi_library(OpenGL) -link_hifi_libraries(shared plugins gpu render-utils) +link_hifi_libraries(shared plugins gpu gl) target_opengl() diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index b4ae6be97f..b2176e0bd1 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -12,8 +12,8 @@ #include #include -#include "gpu/GPUConfig.h" -#include "GLMHelpers.h" +#include +#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 505dae824a..6f6117229a 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 52715ebde7..44ab656f31 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -7,10 +7,10 @@ // #pragma once -#include - #include "DisplayPlugin.h" -#include "OglplusHelpers.h" + +#include +#include class GlWindow; class QOpenGLContext; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp index 1ad61513d6..205444397f 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include "plugins/PluginContainer.h" diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index faf5a7b781..245fd11ef7 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 23bc176d65..6e14a158d4 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 1bdb02fd26..5ba113420d 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index bb90c04c95..7023d285ff 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 render render-utils gl) target_bullet() diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 572b24e99f..7bec8f2b03 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -37,7 +37,7 @@ PolyLineEntityItem(entityItemID, properties) { gpu::PipelinePointer RenderablePolyLineEntityItem::_pipeline; gpu::Stream::FormatPointer RenderablePolyLineEntityItem::_format; gpu::TexturePointer RenderablePolyLineEntityItem::_texture; -GLint RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; +int32_t RenderablePolyLineEntityItem::PAINTSTROKE_GPU_SLOT; void RenderablePolyLineEntityItem::createPipeline() { static const int NORMAL_OFFSET = 12; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 59bf416d7a..2832053639 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -18,7 +18,6 @@ #include "RenderableEntityItem.h" #include #include -#include class RenderablePolyLineEntityItem : public PolyLineEntityItem { @@ -34,7 +33,7 @@ public: static gpu::PipelinePointer _pipeline; static gpu::Stream::FormatPointer _format; static gpu::TexturePointer _texture; - static GLint PAINTSTROKE_GPU_SLOT; + static int32_t PAINTSTROKE_GPU_SLOT; protected: void updateGeometry(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 84707aca3b..29fac6cd84 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libraries/gl/CMakeLists.txt b/libraries/gl/CMakeLists.txt new file mode 100644 index 0000000000..f60e41739a --- /dev/null +++ b/libraries/gl/CMakeLists.txt @@ -0,0 +1,7 @@ +set(TARGET_NAME gl) +setup_hifi_library(OpenGL Qml Quick) +link_hifi_libraries(shared) + +target_glew() +target_opengl() +target_oglplus() \ No newline at end of file diff --git a/libraries/gpu/src/gpu/GPUConfig.h b/libraries/gl/src/gl/Config.h similarity index 100% rename from libraries/gpu/src/gpu/GPUConfig.h rename to libraries/gl/src/gl/Config.h diff --git a/libraries/render-utils/src/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h similarity index 100% rename from libraries/render-utils/src/GLEscrow.h rename to libraries/gl/src/gl/GLEscrow.h diff --git a/libraries/shared/src/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp similarity index 100% rename from libraries/shared/src/GLHelpers.cpp rename to libraries/gl/src/gl/GLHelpers.cpp diff --git a/libraries/shared/src/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h similarity index 100% rename from libraries/shared/src/GLHelpers.h rename to libraries/gl/src/gl/GLHelpers.h diff --git a/libraries/render-utils/src/GlWindow.cpp b/libraries/gl/src/gl/GlWindow.cpp similarity index 94% rename from libraries/render-utils/src/GlWindow.cpp rename to libraries/gl/src/gl/GlWindow.cpp index 248b9305e2..7e43b29e73 100644 --- a/libraries/render-utils/src/GlWindow.cpp +++ b/libraries/gl/src/gl/GlWindow.cpp @@ -8,9 +8,10 @@ #include "GlWindow.h" -#include -#include -#include +#include +#include + +#include "GLHelpers.h" GlWindow::GlWindow(QOpenGLContext* shareContext) : GlWindow(getDefaultOpenGlSurfaceFormat(), shareContext) { } diff --git a/libraries/render-utils/src/GlWindow.h b/libraries/gl/src/gl/GlWindow.h similarity index 100% rename from libraries/render-utils/src/GlWindow.h rename to libraries/gl/src/gl/GlWindow.h diff --git a/libraries/render-utils/src/OffscreenGlCanvas.cpp b/libraries/gl/src/gl/OffscreenGlCanvas.cpp similarity index 94% rename from libraries/render-utils/src/OffscreenGlCanvas.cpp rename to libraries/gl/src/gl/OffscreenGlCanvas.cpp index 07256e7731..e5c1ee4c4a 100644 --- a/libraries/render-utils/src/OffscreenGlCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGlCanvas.cpp @@ -12,10 +12,11 @@ #include "OffscreenGlCanvas.h" -#include -#include -#include -#include +#include +#include +#include + +#include "GLHelpers.h" OffscreenGlCanvas::OffscreenGlCanvas() : _context(new QOpenGLContext), _offscreenSurface(new QOffscreenSurface){ } diff --git a/libraries/render-utils/src/OffscreenGlCanvas.h b/libraries/gl/src/gl/OffscreenGlCanvas.h similarity index 100% rename from libraries/render-utils/src/OffscreenGlCanvas.h rename to libraries/gl/src/gl/OffscreenGlCanvas.h diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp similarity index 99% rename from libraries/render-utils/src/OffscreenQmlSurface.cpp rename to libraries/gl/src/gl/OffscreenQmlSurface.cpp index 6c68b60f42..3afd0c84a8 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -23,7 +23,6 @@ #include "GLEscrow.h" #include "OffscreenGlCanvas.h" -#include "AbstractViewStateInterface.h" // FIXME move to threaded rendering with Qt 5.5 // #define QML_THREADED @@ -207,9 +206,7 @@ private: qreal pixelRatio = 1.0; if (_renderControl && _renderControl->_renderWindow) { pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); - } else { - pixelRatio = AbstractViewStateInterface::instance()->getDevicePixelRatio(); - } + } uvec2 newOffscreenSize = toGlm(newSize * pixelRatio); _textures.setSize(newOffscreenSize); diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h similarity index 100% rename from libraries/render-utils/src/OffscreenQmlSurface.h rename to libraries/gl/src/gl/OffscreenQmlSurface.h diff --git a/libraries/render-utils/src/OglplusHelpers.cpp b/libraries/gl/src/gl/OglplusHelpers.cpp similarity index 100% rename from libraries/render-utils/src/OglplusHelpers.cpp rename to libraries/gl/src/gl/OglplusHelpers.cpp diff --git a/libraries/render-utils/src/OglplusHelpers.h b/libraries/gl/src/gl/OglplusHelpers.h similarity index 100% rename from libraries/render-utils/src/OglplusHelpers.h rename to libraries/gl/src/gl/OglplusHelpers.h diff --git a/libraries/gpu/CMakeLists.txt b/libraries/gpu/CMakeLists.txt index 63da1d8f8a..29115af22f 100644 --- a/libraries/gpu/CMakeLists.txt +++ b/libraries/gpu/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME gpu) AUTOSCRIBE_SHADER_LIB(gpu) setup_hifi_library() -link_hifi_libraries(shared) +link_hifi_libraries(shared gl) target_glew() target_opengl() \ No newline at end of file diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 9cb988a431..6c359d81f1 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -18,7 +18,7 @@ #include #include -#include "GPUConfig.h" +#include #include "Context.h" diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 3fcd556dbb..eed52fccbc 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -19,8 +19,6 @@ #include #include -#include - #include "TextureCache.h" #include "RenderUtilsLogging.h" @@ -573,8 +571,8 @@ void GeometryCache::renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions if (!_gridBuffers.contains(key)) { auto verticesBuffer = std::make_shared(); - GLfloat* vertexData = new GLfloat[vertices * 2]; - GLfloat* vertex = vertexData; + float* vertexData = new float[vertices * 2]; + float* vertex = vertexData; for (int i = 0; i <= xDivisions; i++) { float x = (float)i / xDivisions; @@ -595,7 +593,7 @@ void GeometryCache::renderGrid(gpu::Batch& batch, int xDivisions, int yDivisions *(vertex++) = y; } - verticesBuffer->append(sizeof(GLfloat) * vertices * 2, (gpu::Byte*) vertexData); + verticesBuffer->append(sizeof(float) * vertices * 2, (gpu::Byte*) vertexData); delete[] vertexData; _gridBuffers[key] = verticesBuffer; @@ -674,8 +672,8 @@ void GeometryCache::renderGrid(gpu::Batch& batch, int x, int y, int width, int h _alternateGridBuffers[key] = verticesBuffer; } - GLfloat* vertexData = new GLfloat[vertices * 2]; - GLfloat* vertex = vertexData; + float* vertexData = new float[vertices * 2]; + float* vertex = vertexData; int dx = width / cols; int dy = height / rows; @@ -702,7 +700,7 @@ void GeometryCache::renderGrid(gpu::Batch& batch, int x, int y, int width, int h tx += dx; } - verticesBuffer->append(sizeof(GLfloat) * vertices * 2, (gpu::Byte*) vertexData); + verticesBuffer->append(sizeof(float) * vertices * 2, (gpu::Byte*) vertexData); delete[] vertexData; } @@ -785,8 +783,8 @@ void GeometryCache::updateVertices(int id, const QVector& points, con ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); - GLfloat* vertexData = new GLfloat[details.vertices * FLOATS_PER_VERTEX]; - GLfloat* vertex = vertexData; + float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; + float* vertex = vertexData; int* colorData = new int[details.vertices]; int* colorDataAt = colorData; @@ -798,7 +796,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con *(colorDataAt++) = compactColor; } - details.verticesBuffer->append(sizeof(GLfloat) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -846,8 +844,8 @@ void GeometryCache::updateVertices(int id, const QVector& points, con ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); - GLfloat* vertexData = new GLfloat[details.vertices * FLOATS_PER_VERTEX]; - GLfloat* vertex = vertexData; + float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; + float* vertex = vertexData; int* colorData = new int[details.vertices]; int* colorDataAt = colorData; @@ -860,7 +858,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con *(colorDataAt++) = compactColor; } - details.verticesBuffer->append(sizeof(GLfloat) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -912,8 +910,8 @@ void GeometryCache::updateVertices(int id, const QVector& points, con ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); - GLfloat* vertexData = new GLfloat[details.vertices * FLOATS_PER_VERTEX]; - GLfloat* vertex = vertexData; + float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; + float* vertex = vertexData; int* colorData = new int[details.vertices]; int* colorDataAt = colorData; @@ -930,7 +928,7 @@ void GeometryCache::updateVertices(int id, const QVector& points, con *(colorDataAt++) = compactColor; } - details.verticesBuffer->append(sizeof(GLfloat) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; @@ -997,7 +995,7 @@ void GeometryCache::renderBevelCornersRect(gpu::Batch& batch, int x, int y, int details.stream->addBuffer(details.colorBuffer, 0, details.streamFormat->getChannels().at(1)._stride); - GLfloat vertexBuffer[NUM_FLOATS]; // only vertices, no normals because we're a 2D quad + float vertexBuffer[NUM_FLOATS]; // only vertices, no normals because we're a 2D quad int vertexPoint = 0; // Triangle strip points @@ -1438,8 +1436,8 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, int* colorData = new int[details.vertices]; int* colorDataAt = colorData; - GLfloat* vertexData = new GLfloat[details.vertices * FLOATS_PER_VERTEX]; - GLfloat* vertex = vertexData; + float* vertexData = new float[details.vertices * FLOATS_PER_VERTEX]; + float* vertex = vertexData; glm::vec3 point = start; *(vertex++) = point.x; @@ -1465,7 +1463,7 @@ void GeometryCache::renderDashedLine(gpu::Batch& batch, const glm::vec3& start, *(vertex++) = end.z; *(colorDataAt++) = compactColor; - details.verticesBuffer->append(sizeof(GLfloat) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); + details.verticesBuffer->append(sizeof(float) * FLOATS_PER_VERTEX * details.vertices, (gpu::Byte*) vertexData); details.colorBuffer->append(sizeof(int) * details.vertices, (gpu::Byte*) colorData); delete[] vertexData; delete[] colorData; diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index d4cd1fc2bb..140ca87d0d 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME ui) setup_hifi_library(OpenGL Network Qml Quick Script XmlPatterns) -link_hifi_libraries(render-utils shared) +link_hifi_libraries(shared networking gl) diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 4c8b4a8304..be7f6b5e2e 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -12,7 +12,7 @@ #ifndef hifi_OffscreenUi_h #define hifi_OffscreenUi_h -#include "OffscreenQmlSurface.h" +#include #include #include diff --git a/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 0c183fc2f0..2c9ee23c47 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -3,5 +3,5 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils) # This is not a testcase -- just set it up as a regular hifi project setup_hifi_project(Quick Gui OpenGL Script Widgets) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") -link_hifi_libraries(networking gpu procedural shared fbx model model-networking animation script-engine render-utils ) +link_hifi_libraries(networking gl gpu procedural shared fbx model model-networking animation script-engine render-utils ) copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tests/render-utils/CMakeLists.txt b/tests/render-utils/CMakeLists.txt index b56297d05e..865db4dad5 100644 --- a/tests/render-utils/CMakeLists.txt +++ b/tests/render-utils/CMakeLists.txt @@ -6,6 +6,6 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(render-utils gpu shared) +link_hifi_libraries(render-utils gl gpu shared) copy_dlls_beside_windows_executable() diff --git a/tests/shaders/CMakeLists.txt b/tests/shaders/CMakeLists.txt index 42766c98e3..fd58a5911f 100644 --- a/tests/shaders/CMakeLists.txt +++ b/tests/shaders/CMakeLists.txt @@ -8,8 +8,8 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") #include_oglplus() # link in the shared libraries -link_hifi_libraries(shared octree environment gpu model render fbx networking entities - script-engine physics +link_hifi_libraries(shared octree environment gl gpu model render fbx networking entities + script-engine physics render-utils entities-renderer) include_directories("${PROJECT_BINARY_DIR}/../../libraries/gpu/") diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 8d12663935..3edff67d66 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include "../model/Skybox_vert.h" #include "../model/Skybox_frag.h" diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt index 6a2d8c394a..82fc23e680 100644 --- a/tests/ui/CMakeLists.txt +++ b/tests/ui/CMakeLists.txt @@ -11,6 +11,6 @@ if (WIN32) endif() # link in the shared libraries -link_hifi_libraries(ui render-utils gpu shared) +link_hifi_libraries(shared networking gl gpu ui) copy_dlls_beside_windows_executable() From 133d48ebee8c24cfd1fba989037da20b361573e7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 27 Oct 2015 15:53:48 -0700 Subject: [PATCH 0463/1003] 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 0464/1003] 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 0465/1003] 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 0466/1003] 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 0467/1003] 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 0468/1003] 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 0469/1003] 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 2f835b19fa1c181513412293964ebce6c1f5f30f Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 27 Oct 2015 17:17:19 -0700 Subject: [PATCH 0470/1003] surface is now a model --- .../painting/whiteboard/whiteboardSpawner.js | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 4303803266..037be61459 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -17,13 +17,17 @@ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); //var modelURL = "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx"; -// var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); -var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx"; +var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); +// var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx"; + +var colorIndicatorBorderModelURL = "http://localhost:8080/colorIndicatorBorder.fbx?v1" + Math.random(); + +var surfaceModelURL = "http://localhost:8080/boardSurface.fbx?v1" + Math.random(); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); -var whiteboardDimensions, colorIndicator, eraseAllText +var whiteboardDimensions, colorIndicatorBoxDimensions, colorIndicatorBox, eraseAllText var colorBoxes = []; var colors = [ @@ -44,14 +48,26 @@ var whiteboard = Entities.addEntity({ rotation: rotation, }); +var colorIndicatorPosition = {x: center.x, y: center.y, z: center.z}; +colorIndicatorPosition.y += 1.55; +colorIndicatorPosition = Vec3.sum(colorIndicatorPosition, Vec3.multiply(-0.1, Quat.getFront(rotation))); +var colorIndicatorBorder = Entities.addEntity({ + type: "Model", + position: colorIndicatorPosition, + modelURL: colorIndicatorBorderModelURL, + rotation: rotation, + shapeType: "box" +}); + var surfaceCenter = Vec3.sum(center, Vec3.multiply(-0.1, Quat.getFront(rotation))); -surfaceCenter.y += 0.55; +surfaceCenter.y += 0.6; var drawingSurface = Entities.addEntity({ - type: "Box", - color: {red: 255, green: 255, blue: 255}, + type: "Model", + modelURL: surfaceModelURL, + shapeType: "box", name: "whiteboard surface", position: surfaceCenter, - dimensions: {x: 1.8, y: 1.4, z: 0.01}, + // dimensions: {x: 1.7, y: 1.3, z: 0.01}, script: scriptURL, rotation: rotation, userData: JSON.stringify({ @@ -64,15 +80,18 @@ var drawingSurface = Entities.addEntity({ Script.setTimeout(function() { whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions; + colorIndicatorDimensions = Entities.getEntityProperties(colorIndicatorBorder, "naturalDimensions").naturalDimensions; setUp(); }, 1000) function setUp() { // COLOR INDICATOR BOX + + var colorIndicatorDimensions = { x: whiteboardDimensions.x, - y: 0.05, + y: 0.5, z: 0.02 }; scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); @@ -112,7 +131,7 @@ function setUp() { z: 0.002 }; - var palleteDepthOffset = -0.06; + var palleteDepthOffset = -0.07; var palleteHeightOffset = -0.28; colorBoxPosition = Vec3.sum(colorBoxPosition, Vec3.multiply(palleteDepthOffset, Quat.getFront(rotation))); @@ -139,8 +158,6 @@ function setUp() { colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); } - - var eraseBoxDimensions = { x: 0.5, y: 0.1, @@ -182,6 +199,7 @@ function setUp() { function cleanup() { Entities.deleteEntity(whiteboard); Entities.deleteEntity(drawingSurface); + Entities.deleteEntity(colorIndicatorBorder); Entities.deleteEntity(eraseAllText); Entities.deleteEntity(colorIndicatorBox); colorBoxes.forEach(function(colorBox) { @@ -192,4 +210,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); \ No newline at end of file From ae8938cadcd51cbbe0090b399b270d70cdb87892 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 27 Oct 2015 18:30:35 -0700 Subject: [PATCH 0471/1003] Fix for local eye tracking in AnimGraph Also moved Rig::updateAnimations() now occurs after Rig::updateFromHeadParameters() and Rig::updateFromHandParameters(). This should remove a frame of lag for head and hand IK targets. Rig::updateFromEyeParameters() occurs after Rig::updateAnimations(). But now the eye JointStates are re-computed, this is the actual fix for the local eye tracking issue. --- interface/src/avatar/SkeletonModel.cpp | 43 +++++++++++++++++++------- libraries/animation/src/Rig.cpp | 35 +++------------------ libraries/animation/src/Rig.h | 19 +++++++----- 3 files changed, 47 insertions(+), 50 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 28c7941c52..c3716aa77a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -114,15 +114,12 @@ 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(); if (_owningAvatar->isMyAvatar()) { MyAvatar* myAvatar = static_cast(_owningAvatar); const FBXGeometry& geometry = _geometry->getFBXGeometry(); Rig::HeadParameters headParams; - headParams.modelRotation = getRotation(); - headParams.modelTranslation = getTranslation(); headParams.enableLean = qApp->getAvatarUpdater()->isHMDMode(); headParams.leanSideways = head->getFinalLeanSideways(); headParams.leanForward = head->getFinalLeanForward(); @@ -156,19 +153,13 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); } - headParams.eyeLookAt = head->getLookAtPosition(); - headParams.eyeSaccade = head->getSaccade(); headParams.leanJointIndex = geometry.leanJointIndex; headParams.neckJointIndex = geometry.neckJointIndex; - headParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - headParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; _rig->updateFromHeadParameters(headParams, deltaTime); Rig::HandParameters handParams; - const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX); if (leftPalm && leftPalm->isActive()) { handParams.isLeftEnabled = true; @@ -191,7 +182,28 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); + // evaluate AnimGraph animation and update jointStates. + Model::updateRig(deltaTime, parentTransform); + + Rig::EyeParameters eyeParams; + eyeParams.worldHeadOrientation = headParams.worldHeadOrientation; + eyeParams.eyeLookAt = head->getLookAtPosition(); + eyeParams.eyeSaccade = head->getSaccade(); + eyeParams.modelRotation = getRotation(); + eyeParams.modelTranslation = getTranslation(); + eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + + _rig->updateFromEyeParameters(eyeParams, deltaTime); + + // rebuild the jointState transform for the eyes only + _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform); + _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform); + } else { + + Model::updateRig(deltaTime, parentTransform); + // This is a little more work than we really want. // // Other avatars joint, including their eyes, should already be set just like any other joints @@ -208,9 +220,16 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { head->setBasePitch(glm::degrees(-eulers.x)); head->setBaseYaw(glm::degrees(eulers.y)); head->setBaseRoll(glm::degrees(-eulers.z)); - _rig->updateEyeJoints(geometry.leftEyeJointIndex, geometry.rightEyeJointIndex, - getTranslation(), getRotation(), - head->getFinalOrientationInWorldFrame(), head->getCorrectedLookAtPosition()); + + Rig::EyeParameters eyeParams; + eyeParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); + eyeParams.eyeLookAt = head->getCorrectedLookAtPosition(); + eyeParams.eyeSaccade = glm::vec3(0); + eyeParams.modelRotation = getRotation(); + eyeParams.modelTranslation = getTranslation(); + eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + _rig->updateFromEyeParameters(eyeParams, deltaTime); } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6fd6f5cbf9..023fcb9800 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -22,34 +22,6 @@ #include "AnimSkeleton.h" #include "IKTarget.h" - -void Rig::HeadParameters::dump() const { - qCDebug(animation, "HeadParameters ="); - qCDebug(animation, " leanSideways = %0.5f", (double)leanSideways); - qCDebug(animation, " leanForward = %0.5f", (double)leanForward); - qCDebug(animation, " torsoTwist = %0.5f", (double)torsoTwist); - glm::vec3 axis = glm::axis(localHeadOrientation); - float theta = glm::angle(localHeadOrientation); - qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); - axis = glm::axis(worldHeadOrientation); - theta = glm::angle(worldHeadOrientation); - qCDebug(animation, " localHead pitch = %.5f, yaw = %.5f, roll = %.5f", (double)localHeadPitch, (double)localHeadYaw, (double)localHeadRoll); - qCDebug(animation, " localHeadPosition = (%.5f, %.5f, %.5f)", (double)localHeadPosition.x, (double)localHeadPosition.y, (double)localHeadPosition.z); - qCDebug(animation, " isInHMD = %s", isInHMD ? "true" : "false"); - qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); - axis = glm::axis(modelRotation); - theta = glm::angle(modelRotation); - qCDebug(animation, " modelRotation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); - qCDebug(animation, " modelTranslation = (%.5f, %.5f, %.5f)", (double)modelTranslation.x, (double)modelTranslation.y, (double)modelTranslation.z); - qCDebug(animation, " eyeLookAt = (%.5f, %.5f, %.5f)", (double)eyeLookAt.x, (double)eyeLookAt.y, (double)eyeLookAt.z); - qCDebug(animation, " eyeSaccade = (%.5f, %.5f, %.5f)", (double)eyeSaccade.x, (double)eyeSaccade.y, (double)eyeSaccade.z); - qCDebug(animation, " leanJointIndex = %.d", leanJointIndex); - qCDebug(animation, " neckJointIndex = %.d", neckJointIndex); - qCDebug(animation, " leftEyeJointIndex = %.d", leftEyeJointIndex); - qCDebug(animation, " rightEyeJointIndex = %.d", rightEyeJointIndex); - qCDebug(animation, " isTalking = %s", isTalking ? "true" : "false"); -} - void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { @@ -981,8 +953,6 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { _animVars.unset("lean"); } updateNeckJoint(params.neckJointIndex, params); - updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, - params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); if (_enableAnimGraph) { _animVars.set("isTalking", params.isTalking); @@ -990,6 +960,11 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { } } +void Rig::updateFromEyeParameters(const EyeParameters& params, float dt) { + updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, + params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); +} + static const glm::vec3 X_AXIS(1.0f, 0.0f, 0.0f); static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f); static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c9c42cbbb6..7ae0721625 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -57,24 +57,26 @@ public: float leanForward = 0.0f; // degrees float torsoTwist = 0.0f; // degrees bool enableLean = false; - glm::quat modelRotation = glm::quat(); + glm::quat worldHeadOrientation = glm::quat(); glm::quat localHeadOrientation = glm::quat(); float localHeadPitch = 0.0f; // degrees float localHeadYaw = 0.0f; // degrees float localHeadRoll = 0.0f; // degrees glm::vec3 localHeadPosition = glm::vec3(); bool isInHMD = false; + int leanJointIndex = -1; + int neckJointIndex = -1; + bool isTalking = false; + }; + + struct EyeParameters { glm::quat worldHeadOrientation = glm::quat(); glm::vec3 eyeLookAt = glm::vec3(); // world space glm::vec3 eyeSaccade = glm::vec3(); // world space glm::vec3 modelTranslation = glm::vec3(); - int leanJointIndex = -1; - int neckJointIndex = -1; + glm::quat modelRotation = glm::quat(); int leftEyeJointIndex = -1; int rightEyeJointIndex = -1; - bool isTalking = false; - - void dump() const; }; struct HandParameters { @@ -185,8 +187,7 @@ public: bool getEnableAnimGraph() const { return _enableAnimGraph; } void updateFromHeadParameters(const HeadParameters& params, float dt); - void updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, - const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade = glm::vec3(0.0f)); + void updateFromEyeParameters(const EyeParameters& params, float dt); void updateFromHandParameters(const HandParameters& params, float dt); @@ -207,6 +208,8 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); + void updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, + const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade); QVector _jointStates; int _rootJointIndex = -1; From f9dc05c98919f4287cefc247711ec9d572af797c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 27 Oct 2015 18:59:02 -0700 Subject: [PATCH 0472/1003] Rig::computeMotionAnimationState better filtering of small dt velocities. Only update _lastVelocity when dt is sufficiently large. --- libraries/animation/src/Rig.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index bc47116c30..6afe362146 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -446,7 +446,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos glm::vec3 workingVelocity = _lastVelocity; glm::vec3 positionDelta = worldPosition - _lastPosition; - // don't trust position delta if deltaTime is 'small'. + // Don't trust position delta if deltaTime is 'small'. + // NOTE: This is mostly just a work around for an issue in oculus 0.7 runtime, where + // Application::idle() is being called more frequently and with smaller dt's then expected. const float SMALL_DELTA_TIME = 0.006f; // 6 ms if (deltaTime > SMALL_DELTA_TIME) { workingVelocity = positionDelta / deltaTime; @@ -459,7 +461,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } #endif - _lastVelocity = workingVelocity; + if (deltaTime > SMALL_DELTA_TIME) { + _lastVelocity = workingVelocity; + } if (_enableAnimGraph) { From 06897aa474535c49453fb66db832f256eef49a6a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 Oct 2015 11:53:38 -0700 Subject: [PATCH 0473/1003] don't force parenting of AssetRequest/AssetUpload --- libraries/networking/src/AssetClient.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index b7f1205847..6a1b46340c 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -89,7 +89,6 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) request->moveToThread(thread()); - request->setParent(this); return request; } else { @@ -105,7 +104,6 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { auto upload = new AssetUpload(filename); upload->moveToThread(thread()); - upload->setParent(this); return upload; } else { @@ -118,7 +116,6 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& ex auto upload = new AssetUpload(data, extension); upload->moveToThread(thread()); - upload->setParent(this); return upload; } else { From f91dc93620a331a72953978d6d6b78e7a3964396 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 08:40:38 -0700 Subject: [PATCH 0474/1003] adjust locking in EntityItem::getActionDataInternal --- libraries/entities/src/EntityItem.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 1fd9acc1e2..701732b921 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1790,8 +1790,16 @@ const QByteArray EntityItem::getActionDataInternal() const { const QByteArray EntityItem::getActionData() const { QByteArray result; assertUnlocked(); + + if (_actionDataDirty) { + EntityItem* unconstThis = const_cast(this); + unconstThis->withWriteLock([&] { + getActionDataInternal(); + }); + } + withReadLock([&] { - result = getActionDataInternal(); + result = _allActionsDataCache; }); return result; } From 92ca658aae617ba217202afc58afc4c894bb8c42 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 09:23:20 -0700 Subject: [PATCH 0475/1003] 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 0476/1003] 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 0477/1003] 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 afec2a9771cadd729f21aca42020fada86e898cd Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 28 Oct 2015 09:42:55 -0700 Subject: [PATCH 0478/1003] fix whitespace --- interface/src/avatar/MyAvatar.h | 6 +++--- libraries/physics/src/CharacterController.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 9e66472328..5f75b7cf14 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -164,7 +164,7 @@ public: void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(); - const QString& getCollisionSoundURL() {return _collisionSoundURL; } + const QString& getCollisionSoundURL() { return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); void clearScriptableSettings(); @@ -364,9 +364,9 @@ private: glm::quat _customListenOrientation; bool _isFollowingHMD { false }; - float _followHMDAlpha{0.0f}; + float _followHMDAlpha { 0.0f }; - quint64 _lastUpdateFromHMDTime{usecTimestampNow()}; + quint64 _lastUpdateFromHMDTime { usecTimestampNow() }; AtRestDetector _hmdAtRestDetector; glm::vec3 _lastPosition; bool _lastIsMoving { false }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 9ba22cf2db..aadf9b16ea 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -32,7 +32,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } if (world && _rigidBody) { _dynamicsWorld = world; - _pendingFlags &= ~ PENDING_FLAG_JUMP; + _pendingFlags &= ~PENDING_FLAG_JUMP; _dynamicsWorld->addRigidBody(_rigidBody, COLLISION_GROUP_MY_AVATAR, COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); //reset(_dynamicsWorld); @@ -46,7 +46,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; } } else { - _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; + _pendingFlags &= ~PENDING_FLAG_REMOVE_FROM_SIMULATION; } } From 2b5b4f111823c4858c78be41aa371144c70fc1c3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 09:54:16 -0700 Subject: [PATCH 0479/1003] avoid unneeded read-lock if action-data was dirty --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 701732b921..08e63fb1d1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1795,6 +1795,7 @@ const QByteArray EntityItem::getActionData() const { EntityItem* unconstThis = const_cast(this); unconstThis->withWriteLock([&] { getActionDataInternal(); + return _allActionsDataCache; }); } From d504f449e4754b3d88030888d1c48d30e5dc019d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 09:55:11 -0700 Subject: [PATCH 0480/1003] undo last commit -- avoid unneeded read-lock if action-data was dirty --- libraries/entities/src/EntityItem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 08e63fb1d1..701732b921 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1795,7 +1795,6 @@ const QByteArray EntityItem::getActionData() const { EntityItem* unconstThis = const_cast(this); unconstThis->withWriteLock([&] { getActionDataInternal(); - return _allActionsDataCache; }); } From 08a0bf33a49833eb080c42b531634c61ed4e05b7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 09:55:55 -0700 Subject: [PATCH 0481/1003] avoid unneeded read-lock if action-data was dirty --- libraries/entities/src/EntityItem.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 701732b921..91ea4864df 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1795,12 +1795,13 @@ const QByteArray EntityItem::getActionData() const { EntityItem* unconstThis = const_cast(this); unconstThis->withWriteLock([&] { getActionDataInternal(); + result = _allActionsDataCache; + }); + } else { + withReadLock([&] { + result = _allActionsDataCache; }); } - - withReadLock([&] { - result = _allActionsDataCache; - }); return result; } From f6a0004f26f73ec7c506ec3d7a287fbd6799f29b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 10:45:24 -0700 Subject: [PATCH 0482/1003] get rid of some useless consts --- libraries/entities/src/EntityItem.cpp | 4 ++-- libraries/entities/src/EntityItem.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 91ea4864df..4d5fb1b180 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1776,7 +1776,7 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { return; } -const QByteArray EntityItem::getActionDataInternal() const { +QByteArray EntityItem::getActionDataInternal() const { if (_actionDataDirty) { bool success; serializeActions(success, _allActionsDataCache); @@ -1787,7 +1787,7 @@ const QByteArray EntityItem::getActionDataInternal() const { return _allActionsDataCache; } -const QByteArray EntityItem::getActionData() const { +QByteArray EntityItem::getActionData() const { QByteArray result; assertUnlocked(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 858dc7e326..9e6430b4c3 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -401,7 +401,7 @@ public: bool removeAction(EntitySimulation* simulation, const QUuid& actionID); bool clearActions(EntitySimulation* simulation); void setActionData(QByteArray actionData); - const QByteArray getActionData() const; + QByteArray getActionData() const; bool hasActions() { return !_objectActions.empty(); } QList getActionIDs() { return _objectActions.keys(); } QVariantMap getActionArguments(const QUuid& actionID) const; @@ -415,7 +415,7 @@ public: protected: - const QByteArray getActionDataInternal() const; + QByteArray getActionDataInternal() const; void setActionDataInternal(QByteArray actionData); static bool _sendPhysicsUpdates; From b3734b5ddb3e44c6d5185abb86a3a0eb1992a456 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 28 Oct 2015 10:46:09 -0700 Subject: [PATCH 0483/1003] Rig: removed redundant method updateEyeJoints() Instead we call updateEyeJoint() twice, once for each eye. --- libraries/animation/src/Rig.cpp | 12 ++++-------- libraries/animation/src/Rig.h | 2 -- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 023fcb9800..8c7fa22c22 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -961,8 +961,10 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { } void Rig::updateFromEyeParameters(const EyeParameters& params, float dt) { - updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, - params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, + params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); + updateEyeJoint(params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, + params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); } static const glm::vec3 X_AXIS(1.0f, 0.0f, 0.0f); @@ -1120,12 +1122,6 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { } } -void Rig::updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, - const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { - updateEyeJoint(leftEyeIndex, modelTranslation, modelRotation, worldHeadOrientation, lookAtSpot, saccade); - updateEyeJoint(rightEyeIndex, modelTranslation, modelRotation, worldHeadOrientation, lookAtSpot, saccade); -} - void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { auto& state = _jointStates[index]; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7ae0721625..25e823d6bc 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -208,8 +208,6 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); - void updateEyeJoints(int leftEyeIndex, int rightEyeIndex, const glm::vec3& modelTranslation, const glm::quat& modelRotation, - const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade); QVector _jointStates; int _rootJointIndex = -1; From 367175b8a64e6ef9e5953861047479fb53a07241 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 28 Oct 2015 10:49:19 -0700 Subject: [PATCH 0484/1003] Reduce lock time. --- interface/src/avatar/AvatarManager.cpp | 7 +- interface/src/avatar/MyAvatar.cpp | 110 +++++++++++++------------ 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4352934315..9783590b05 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -355,9 +355,6 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) if (sessionID == _myAvatar->getSessionUUID()) { return std::static_pointer_cast(_myAvatar); } - AvatarSharedPointer avatar; - withAvatarHash([&avatar, &sessionID] (const AvatarHash& hash) { - avatar = hash[sessionID]; - }); - return avatar; + QReadLocker locker(&_hashLock); + return _avatarHash[sessionID]; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c6711c3324..a69c22813a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1018,72 +1018,74 @@ void MyAvatar::updateLookAtTargetAvatar() { const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f; const float GREATEST_LOOKING_AT_DISTANCE = 10.0f; - DependencyManager::get()->withAvatarHash([&] (const AvatarHash& hash) { - foreach (const AvatarSharedPointer& avatarPointer, hash) { - auto avatar = static_pointer_cast(avatarPointer); - bool isCurrentTarget = avatar->getIsLookAtTarget(); - float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); - avatar->setIsLookAtTarget(false); - if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getScale())) { - float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); - if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { - _lookAtTargetAvatar = avatarPointer; - _targetAvatarPosition = avatarPointer->getPosition(); - smallestAngleTo = angleTo; - } - if (isLookingAtMe(avatar)) { + AvatarHash hash; + DependencyManager::get()->withAvatarHash([&] (const AvatarHash& locked) { + hash = locked; // make a shallow copy and operate on that, to minimize lock time + }); + foreach (const AvatarSharedPointer& avatarPointer, hash) { + auto avatar = static_pointer_cast(avatarPointer); + bool isCurrentTarget = avatar->getIsLookAtTarget(); + float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition); + avatar->setIsLookAtTarget(false); + if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getScale())) { + float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition)); + if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) { + _lookAtTargetAvatar = avatarPointer; + _targetAvatarPosition = avatarPointer->getPosition(); + smallestAngleTo = angleTo; + } + if (isLookingAtMe(avatar)) { - // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. - glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. + // Alter their gaze to look directly at my camera; this looks more natural than looking at my avatar's face. + glm::vec3 lookAtPosition = avatar->getHead()->getLookAtPosition(); // A position, in world space, on my avatar. - // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) - // Let's get everything to world space: - glm::vec3 avatarLeftEye = getHead()->getLeftEyePosition(); - glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); - // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. - // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) - // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. - glm::mat4 leftEye = qApp->getEyeOffset(Eye::Left); - glm::mat4 rightEye = qApp->getEyeOffset(Eye::Right); - glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); - glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); - auto humanSystem = qApp->getViewFrustum(); - glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); - glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); + // The camera isn't at the point midway between the avatar eyes. (Even without an HMD, the head can be offset a bit.) + // Let's get everything to world space: + glm::vec3 avatarLeftEye = getHead()->getLeftEyePosition(); + glm::vec3 avatarRightEye = getHead()->getRightEyePosition(); + // When not in HMD, these might both answer identity (i.e., the bridge of the nose). That's ok. + // By my inpsection of the code and live testing, getEyeOffset and getEyePose are the same. (Application hands identity as offset matrix.) + // This might be more work than needed for any given use, but as we explore different formulations, we go mad if we don't work in world space. + glm::mat4 leftEye = qApp->getEyeOffset(Eye::Left); + glm::mat4 rightEye = qApp->getEyeOffset(Eye::Right); + glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]); + glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]); + auto humanSystem = qApp->getViewFrustum(); + glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal); + glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal); - // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. - // (We will be adding that offset to the camera position, after making some other adjustments.) - glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); + // First find out where (in world space) the person is looking relative to that bridge-of-the-avatar point. + // (We will be adding that offset to the camera position, after making some other adjustments.) + glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition(); - // Scale by proportional differences between avatar and human. - float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye); - float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); - gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; + // Scale by proportional differences between avatar and human. + float humanEyeSeparationInModelSpace = glm::length(humanLeftEye - humanRightEye); + float avatarEyeSeparation = glm::length(avatarLeftEye - avatarRightEye); + gazeOffset = gazeOffset * humanEyeSeparationInModelSpace / avatarEyeSeparation; - // If the camera is also not oriented with the head, adjust by getting the offset in head-space... - /* Not needed (i.e., code is a no-op), but I'm leaving the example code here in case something like this is needed someday. - glm::quat avatarHeadOrientation = getHead()->getOrientation(); - glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; - // ... and treat that as though it were in camera space, bringing it back to world space. - // But camera is fudged to make the picture feel like the avatar's orientation. - glm::quat humanOrientation = humanSystem->getOrientation(); // or just avatar getOrienation() ? - gazeOffset = humanOrientation * gazeOffsetLocalToHead; - glm::vec3 corrected = humanSystem->getPosition() + gazeOffset; - */ + // If the camera is also not oriented with the head, adjust by getting the offset in head-space... + /* Not needed (i.e., code is a no-op), but I'm leaving the example code here in case something like this is needed someday. + glm::quat avatarHeadOrientation = getHead()->getOrientation(); + glm::vec3 gazeOffsetLocalToHead = glm::inverse(avatarHeadOrientation) * gazeOffset; + // ... and treat that as though it were in camera space, bringing it back to world space. + // But camera is fudged to make the picture feel like the avatar's orientation. + glm::quat humanOrientation = humanSystem->getOrientation(); // or just avatar getOrienation() ? + gazeOffset = humanOrientation * gazeOffsetLocalToHead; + glm::vec3 corrected = humanSystem->getPosition() + gazeOffset; + */ - // And now we can finally add that offset to the camera. - glm::vec3 corrected = qApp->getViewFrustum()->getPosition() + gazeOffset; + // And now we can finally add that offset to the camera. + glm::vec3 corrected = qApp->getViewFrustum()->getPosition() + gazeOffset; - avatar->getHead()->setCorrectedLookAtPosition(corrected); + avatar->getHead()->setCorrectedLookAtPosition(corrected); - } else { - avatar->getHead()->clearCorrectedLookAtPosition(); - } } else { avatar->getHead()->clearCorrectedLookAtPosition(); } + } else { + avatar->getHead()->clearCorrectedLookAtPosition(); } - }); + } auto avatarPointer = _lookAtTargetAvatar.lock(); if (avatarPointer) { static_pointer_cast(avatarPointer)->setIsLookAtTarget(true); From a1096510e80dc566ec9be49411099cc11c1584a2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 10:52:01 -0700 Subject: [PATCH 0485/1003] put some useless consts back --- libraries/entities/src/EntityItem.cpp | 4 ++-- libraries/entities/src/EntityItem.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4d5fb1b180..91ea4864df 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1776,7 +1776,7 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { return; } -QByteArray EntityItem::getActionDataInternal() const { +const QByteArray EntityItem::getActionDataInternal() const { if (_actionDataDirty) { bool success; serializeActions(success, _allActionsDataCache); @@ -1787,7 +1787,7 @@ QByteArray EntityItem::getActionDataInternal() const { return _allActionsDataCache; } -QByteArray EntityItem::getActionData() const { +const QByteArray EntityItem::getActionData() const { QByteArray result; assertUnlocked(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9e6430b4c3..858dc7e326 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -401,7 +401,7 @@ public: bool removeAction(EntitySimulation* simulation, const QUuid& actionID); bool clearActions(EntitySimulation* simulation); void setActionData(QByteArray actionData); - QByteArray getActionData() const; + const QByteArray getActionData() const; bool hasActions() { return !_objectActions.empty(); } QList getActionIDs() { return _objectActions.keys(); } QVariantMap getActionArguments(const QUuid& actionID) const; @@ -415,7 +415,7 @@ public: protected: - QByteArray getActionDataInternal() const; + const QByteArray getActionDataInternal() const; void setActionDataInternal(QByteArray actionData); static bool _sendPhysicsUpdates; From 6ae5c540fdeb914ee6538de64f62056ba202ca74 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 28 Oct 2015 11:09:24 -0700 Subject: [PATCH 0486/1003] SkeletonModel: coding standard fix Replaced glm::vec3(0) with glm::vec3(). --- interface/src/avatar/SkeletonModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c3716aa77a..04be8a1f63 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -224,7 +224,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::EyeParameters eyeParams; eyeParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); eyeParams.eyeLookAt = head->getCorrectedLookAtPosition(); - eyeParams.eyeSaccade = glm::vec3(0); + eyeParams.eyeSaccade = glm::vec3(); eyeParams.modelRotation = getRotation(); eyeParams.modelTranslation = getTranslation(); eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; From 457ec76d3d85f69e6153b66b1d63afa0c73ab432 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 28 Oct 2015 11:21:53 -0700 Subject: [PATCH 0487/1003] 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 0488/1003] 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 0489/1003] 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 0490/1003] 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 40277823354d9ecd526628d799982356959d7e00 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 28 Oct 2015 13:18:57 -0700 Subject: [PATCH 0491/1003] Fixed issue with overlays disapearing on release grab --- .../whiteboard/whiteboardEntityScript.js | 4 + .../painting/whiteboard/whiteboardSpawner.js | 88 ++++++++----------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index f38073f389..fffaa78a8c 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -47,6 +47,7 @@ if (this.painting) { return; } + this.whichHand = this.hand; if (this.hand === RIGHT_HAND) { this.getHandPosition = MyAvatar.getRightPalmPosition; this.getHandRotation = MyAvatar.getRightPalmRotation; @@ -183,6 +184,9 @@ }, releaseGrab: function() { + if(this.hand !== this.whichHand) { + return; + } this.stopPainting(); }, diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 037be61459..c40a763bf6 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -22,12 +22,14 @@ var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); var colorIndicatorBorderModelURL = "http://localhost:8080/colorIndicatorBorder.fbx?v1" + Math.random(); +var eraseModelURL = "http://localhost:8080/eraser.fbx?v1" + Math.random(); + var surfaceModelURL = "http://localhost:8080/boardSurface.fbx?v1" + Math.random(); var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); -var whiteboardDimensions, colorIndicatorBoxDimensions, colorIndicatorBox, eraseAllText +var whiteboardDimensions, colorIndicatorBoxDimensions, colorIndicatorBox, eraser; var colorBoxes = []; var colors = [ @@ -48,7 +50,11 @@ var whiteboard = Entities.addEntity({ rotation: rotation, }); -var colorIndicatorPosition = {x: center.x, y: center.y, z: center.z}; +var colorIndicatorPosition = { + x: center.x, + y: center.y, + z: center.z +}; colorIndicatorPosition.y += 1.55; colorIndicatorPosition = Vec3.sum(colorIndicatorPosition, Vec3.multiply(-0.1, Quat.getFront(rotation))); var colorIndicatorBorder = Entities.addEntity({ @@ -60,27 +66,40 @@ var colorIndicatorBorder = Entities.addEntity({ }); var surfaceCenter = Vec3.sum(center, Vec3.multiply(-0.1, Quat.getFront(rotation))); -surfaceCenter.y += 0.6; +surfaceCenter.y += 0.6; var drawingSurface = Entities.addEntity({ type: "Model", modelURL: surfaceModelURL, shapeType: "box", name: "whiteboard surface", position: surfaceCenter, - // dimensions: {x: 1.7, y: 1.3, z: 0.01}, script: scriptURL, - rotation: rotation, - userData: JSON.stringify({ + rotation: rotation, + userData: JSON.stringify({ color: { currentColor: colors[0] } }) -}) +}); + +var eraseModelPosition = Vec3.sum(center, {x: 0, y: 2, z: 0 }); +scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); +var eraser = Entities.addEntity({ + type: "Model", + modelURL: eraseModelURL, + position: eraseModelPosition, + name: "Eraser", + script: scriptURL, + rotation: rotation, + userData: JSON.stringify({ + whiteboard: drawingSurface + }) +}); Script.setTimeout(function() { whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions; - colorIndicatorDimensions = Entities.getEntityProperties(colorIndicatorBorder, "naturalDimensions").naturalDimensions; + colorIndicatorBorderDimensions = Entities.getEntityProperties(colorIndicatorBorder, "naturalDimensions").naturalDimensions; setUp(); }, 1000) @@ -89,24 +108,25 @@ function setUp() { // COLOR INDICATOR BOX - var colorIndicatorDimensions = { - x: whiteboardDimensions.x, - y: 0.5, - z: 0.02 - }; + var eraseModelDimensions = Entities.getEntityProperties(eraser, "naturalDimensions").naturalDimensions; + Entities.editEntity(eraser, {dimensions: eraseModelDimensions}); + Entities.editEntity(colorIndicatorBorder, {dimensions: colorIndicatorBorderDimensions}); + scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); var colorIndicatorPosition = Vec3.sum(center, { x: 0, - y: whiteboardDimensions.y / 2 + colorIndicatorDimensions.y / 2, + y: whiteboardDimensions.y / 2 + colorIndicatorBorderDimensions.y / 2, z: 0 }); + colorIndicatorPosition = Vec3.sum(colorIndicatorPosition, Vec3.multiply(-.1, Quat.getFront(rotation))); + var colorIndicatorBoxDimensions = Vec3.multiply(colorIndicatorBorderDimensions, 0.9); colorIndicatorBox = Entities.addEntity({ type: "Box", name: "Color Indicator", color: colors[0], rotation: rotation, position: colorIndicatorPosition, - dimensions: colorIndicatorDimensions, + dimensions: colorIndicatorBoxDimensions, script: scriptURL, userData: JSON.stringify({ whiteboard: drawingSurface @@ -158,49 +178,13 @@ function setUp() { colorBoxPosition = Vec3.sum(colorBoxPosition, spaceBetweenColorBoxes); } - var eraseBoxDimensions = { - x: 0.5, - y: 0.1, - z: 0.01 - }; - - - var eraseBoxPosition = Vec3.sum(center, Vec3.multiply(direction, whiteboardDimensions.x / 2 + eraseBoxDimensions.x / 2 + 0.01)); - eraseBoxPosition.y += 0.3; - scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); - eraseAllText = Entities.addEntity({ - type: "Text", - position: eraseBoxPosition, - name: "Eraser", - script: scriptURL, - rotation: rotation, - dimensions: eraseBoxDimensions, - backgroundColor: { - red: 0, - green: 60, - blue: 0 - }, - textColor: { - red: 255, - green: 10, - blue: 10 - }, - text: "ERASE BOARD", - lineHeight: 0.07, - userData: JSON.stringify({ - whiteboard: drawingSurface - }) - }); - - - } function cleanup() { Entities.deleteEntity(whiteboard); Entities.deleteEntity(drawingSurface); Entities.deleteEntity(colorIndicatorBorder); - Entities.deleteEntity(eraseAllText); + Entities.deleteEntity(eraser); Entities.deleteEntity(colorIndicatorBox); colorBoxes.forEach(function(colorBox) { Entities.deleteEntity(colorBox); From b90af1a1ce636f6dc66d098011b8a8ac508643ab Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 13:21:45 -0700 Subject: [PATCH 0492/1003] 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 ac2effb33cce4e55c9c91e0b3263cd0177da4979 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 28 Oct 2015 13:32:10 -0700 Subject: [PATCH 0493/1003] Not erasing board, models hosted on s3 --- .../whiteboard/whiteboardEntityScript.js | 4 +--- .../painting/whiteboard/whiteboardSpawner.js | 21 +++++++------------ 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index fffaa78a8c..ce0e8a82b1 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -243,12 +243,10 @@ unload: function() { Overlays.deleteOverlay(this.laserPointer); - // this.eraseBoard(); + this.eraseBoard(); } - }; - // entity scripts always need to return a newly constructed object of our type return new Whiteboard(); }); \ No newline at end of file diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index c40a763bf6..573432b77a 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -16,15 +16,11 @@ Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath("whiteboardEntityScript.js"); -//var modelURL = "https://hifi-public.s3.amazonaws.com/ozan/support/for_eric/whiteboard/whiteboard.fbx"; -var modelURL = "http://localhost:8080/whiteboard.fbx?v1" + Math.random(); -// var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx"; +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/whiteboard.fbx"; -var colorIndicatorBorderModelURL = "http://localhost:8080/colorIndicatorBorder.fbx?v1" + Math.random(); - -var eraseModelURL = "http://localhost:8080/eraser.fbx?v1" + Math.random(); - -var surfaceModelURL = "http://localhost:8080/boardSurface.fbx?v1" + Math.random(); +var colorIndicatorBorderModelURL = "https://s3.amazonaws.com/hifi-public/eric/models/colorIndicatorBorder.fbx"; +var eraserModelURL = "https://s3.amazonaws.com/hifi-public/eric/models/eraser.fbx"; +var surfaceModelURL = "https://s3.amazonaws.com/hifi-public/eric/models/boardSurface.fbx"; var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); @@ -87,7 +83,7 @@ var eraseModelPosition = Vec3.sum(center, {x: 0, y: 2, z: 0 }); scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraser = Entities.addEntity({ type: "Model", - modelURL: eraseModelURL, + modelURL: eraserModelURL, position: eraseModelPosition, name: "Eraser", script: scriptURL, @@ -101,13 +97,10 @@ Script.setTimeout(function() { whiteboardDimensions = Entities.getEntityProperties(whiteboard, "naturalDimensions").naturalDimensions; colorIndicatorBorderDimensions = Entities.getEntityProperties(colorIndicatorBorder, "naturalDimensions").naturalDimensions; setUp(); -}, 1000) +}, 2000) function setUp() { - // COLOR INDICATOR BOX - - var eraseModelDimensions = Entities.getEntityProperties(eraser, "naturalDimensions").naturalDimensions; Entities.editEntity(eraser, {dimensions: eraseModelDimensions}); Entities.editEntity(colorIndicatorBorder, {dimensions: colorIndicatorBorderDimensions}); @@ -194,4 +187,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -Script.scriptEnding.connect(cleanup); \ No newline at end of file +// Script.scriptEnding.connect(cleanup); \ No newline at end of file From 22d6b6df34526da4be267bdafbeeae188c91fdb0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 28 Oct 2015 13:49:52 -0700 Subject: [PATCH 0494/1003] make withWriteLock and withTryWriteLock const --- libraries/entities/src/EntityItem.cpp | 3 +-- libraries/shared/src/shared/ReadWriteLockable.h | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 91ea4864df..f012ba6eee 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1792,8 +1792,7 @@ const QByteArray EntityItem::getActionData() const { assertUnlocked(); if (_actionDataDirty) { - EntityItem* unconstThis = const_cast(this); - unconstThis->withWriteLock([&] { + withWriteLock([&] { getActionDataInternal(); result = _allActionsDataCache; }); diff --git a/libraries/shared/src/shared/ReadWriteLockable.h b/libraries/shared/src/shared/ReadWriteLockable.h index a7d30d562b..98dff4a841 100644 --- a/libraries/shared/src/shared/ReadWriteLockable.h +++ b/libraries/shared/src/shared/ReadWriteLockable.h @@ -14,7 +14,7 @@ class ReadWriteLockable { public: template - bool withWriteLock(F f, bool require = true) { + bool withWriteLock(F f, bool require = true) const { if (!require) { bool result = _lock.tryLockForWrite(); if (result) { @@ -22,7 +22,7 @@ public: _lock.unlock(); } return result; - } + } QWriteLocker locker(&_lock); f(); @@ -30,7 +30,7 @@ public: } template - bool withTryWriteLock(F f) { + bool withTryWriteLock(F f) const { return withWriteLock(f, false); } From bc0e14cb71275d36459f17f7deb00fdb0bc390b3 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 28 Oct 2015 13:58:44 -0700 Subject: [PATCH 0495/1003] Don't render avatar's renderBoundingCollisionShapes before the data is there. (Found while trying to repro "Deadlock in AvatarData::nextAttitude() on main thread" https://app.asana.com/0/32622044445063/61023569045356) --- interface/src/avatar/Avatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9f4e7ee3cf..00bcf1d271 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -454,7 +454,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { */ bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); - if (renderBounding && shouldRenderHead(renderArgs)) { + if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) { _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } From 6d99a822a3c969993ce1abae47ad08a4c2960cf8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 28 Oct 2015 14:33:50 -0700 Subject: [PATCH 0496/1003] use a case-insensitive search in allowed editors QStringList --- domain-server/src/DomainGatekeeper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 90b22ffdd8..d360ab4802 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -226,7 +226,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // if the allowed editors list is empty then everyone can adjust locks bool canAdjustLocks = allowedEditors.empty(); - if (allowedEditors.contains(username)) { + if (allowedEditors.contains(username, Qt::CaseInsensitive)) { // we have a non-empty allowed editors list - check if this user is verified to be in it if (!verifiedUsername) { if (!verifyUserSignature(username, usernameSignature, HifiSockAddr())) { From e9bdb872bdd8d3c78a6c0f6e3f28b2b407c64bc7 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 28 Oct 2015 15:25:11 -0700 Subject: [PATCH 0497/1003] added light to whiteboard --- examples/painting/whiteboard/whiteboardSpawner.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 573432b77a..b08c9bd624 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -79,6 +79,14 @@ var drawingSurface = Entities.addEntity({ }); +var lightPosition = Vec3.sum(center, Vec3.multiply(-2, Quat.getFront(rotation))); +var light = Entities.addEntity({ + type: 'Light', + position: lightPosition, + dimensions: {x: 5, y: 5, z: 5}, + color: {red: 255, green: 255, blue: 255} +}); + var eraseModelPosition = Vec3.sum(center, {x: 0, y: 2, z: 0 }); scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraser = Entities.addEntity({ @@ -179,6 +187,7 @@ function cleanup() { Entities.deleteEntity(colorIndicatorBorder); Entities.deleteEntity(eraser); Entities.deleteEntity(colorIndicatorBox); + Entities.deleteEntity(light); colorBoxes.forEach(function(colorBox) { Entities.deleteEntity(colorBox); }); From 8e83943efd4840f25166c2d12ffa482f38938d12 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 28 Oct 2015 15:43:36 -0700 Subject: [PATCH 0498/1003] Don't play other avatar's collision sounds. https://app.asana.com/0/32622044445063/61104546898252 --- libraries/physics/src/PhysicsEngine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index d6772f8d36..0efa0c0f39 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -352,16 +352,16 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { glm::vec3 velocityChange = (motionStateA ? motionStateA->getObjectLinearVelocityChange() : glm::vec3(0.0f)) + (motionStateB ? motionStateB->getObjectLinearVelocityChange() : glm::vec3(0.0f)); - if (motionStateA && motionStateA->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (motionStateA) { QUuid idA = motionStateA->getObjectID(); QUuid idB; - if (motionStateB && motionStateB->getType() == MOTIONSTATE_TYPE_ENTITY) { + if (motionStateB) { idB = motionStateB->getObjectID(); } glm::vec3 position = bulletToGLM(contact.getPositionWorldOnB()) + _originOffset; glm::vec3 penetration = bulletToGLM(contact.distance * contact.normalWorldOnB); _collisionEvents.push_back(Collision(type, idA, idB, position, penetration, velocityChange)); - } else if (motionStateB && motionStateB->getType() == MOTIONSTATE_TYPE_ENTITY) { + } else if (motionStateB) { QUuid idB = motionStateB->getObjectID(); glm::vec3 position = bulletToGLM(contact.getPositionWorldOnA()) + _originOffset; // NOTE: we're flipping the order of A and B (so that the first objectID is never NULL) From 1b1490bb75403a6f31040d234035c5af67092c76 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 28 Oct 2015 16:02:15 -0700 Subject: [PATCH 0499/1003] added blocker so user cant get so close to whiteboard --- .../painting/whiteboard/whiteboardSpawner.js | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index b08c9bd624..29183dcc76 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -25,7 +25,7 @@ var rotation = Quat.safeEulerAngles(Camera.getOrientation()); rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(rotation))); -var whiteboardDimensions, colorIndicatorBoxDimensions, colorIndicatorBox, eraser; +var whiteboardDimensions, colorIndicatorBoxDimensions, colorIndicatorBox, eraser, blocker; var colorBoxes = []; var colors = [ @@ -87,12 +87,13 @@ var light = Entities.addEntity({ color: {red: 255, green: 255, blue: 255} }); -var eraseModelPosition = Vec3.sum(center, {x: 0, y: 2, z: 0 }); +var eraserPosition = Vec3.sum(center, {x: 0, y: 2.05, z: 0 }); +eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.1, rotation)); scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraser = Entities.addEntity({ type: "Model", modelURL: eraserModelURL, - position: eraseModelPosition, + position: eraserPosition, name: "Eraser", script: scriptURL, rotation: rotation, @@ -109,6 +110,17 @@ Script.setTimeout(function() { function setUp() { + var blockerPosition = Vec3.sum(center, {x: 0, y: -1, z: 0 }); + blockerPosition = Vec3.sum(blockerPosition, Vec3.multiply(-1, Quat.getFront(rotation))); + blocker = Entities.addEntity({ + type: "Box", + rotation: rotation, + position: blockerPosition, + dimensions: {x: whiteboardDimensions.x, y: 1, z: 0.1}, + shapeType: "box", + visible: false + }); + var eraseModelDimensions = Entities.getEntityProperties(eraser, "naturalDimensions").naturalDimensions; Entities.editEntity(eraser, {dimensions: eraseModelDimensions}); Entities.editEntity(colorIndicatorBorder, {dimensions: colorIndicatorBorderDimensions}); @@ -187,6 +199,7 @@ function cleanup() { Entities.deleteEntity(colorIndicatorBorder); Entities.deleteEntity(eraser); Entities.deleteEntity(colorIndicatorBox); + Entities.deleteEntity(blocker); Entities.deleteEntity(light); colorBoxes.forEach(function(colorBox) { Entities.deleteEntity(colorBox); @@ -196,4 +209,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 7695afec0af14260393214fbb797a47fd7c540e3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 28 Oct 2015 16:42:17 -0700 Subject: [PATCH 0500/1003] Fix ScriptEngine crash --- libraries/script-engine/src/ScriptEngine.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 1d6bf32fcc..39f0963112 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -685,9 +685,8 @@ void ScriptEngine::run() { } lastUpdate = now; - if (hadUncauchtExceptions(*this, _fileNameString)) { - stop(); - } + // Debug and clear exceptions + hadUncauchtExceptions(*this, _fileNameString); } stopAllTimers(); // make sure all our timers are stopped if the script is ending From 62e56d3f131443908d05a731a07e6b6472ec9fae Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 28 Oct 2015 16:44:53 -0700 Subject: [PATCH 0501/1003] Don't go to wrong position on startup/teleport. This fixes one cause of being in the wrong place. https://app.asana.com/0/32622044445063/61787931469907 --- interface/src/avatar/MyAvatar.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f7fad2bd2b..4d860b7acd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -208,6 +208,11 @@ void MyAvatar::update(float deltaTime) { setPosition(_goToPosition); setOrientation(_goToOrientation); _goToPending = false; + // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes + // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). + // However, render/MyAvatar::update/Application::update don't always match (e.g., when using the separate avatar update thread), + // so we update now. It's ok if it updates again in the normal way. + updateSensorToWorldMatrix(); } if (_referential) { From a5b95d73073ec1ebae2780eb2bf15d071d2e3f69 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 16:46:56 -0700 Subject: [PATCH 0502/1003] Fixing the entity properties view with the new properties group --- examples/html/entityProperties.html | 35 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index cffca523d7..a8eff20df7 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -328,6 +328,7 @@ var elZoneSections = document.querySelectorAll(".zone-section"); allSections.push(elZoneSections); var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled"); + var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color"); var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red"); var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green"); @@ -337,6 +338,7 @@ var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z"); + var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url"); var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude"); var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude"); @@ -568,15 +570,17 @@ } elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled; - elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLightColor.red + "," + properties.keyLightColor.green + "," + properties.keyLightColor.blue + ")"; - elZoneKeyLightColorRed.value = properties.keyLightColor.red; - elZoneKeyLightColorGreen.value = properties.keyLightColor.green; - elZoneKeyLightColorBlue.value = properties.keyLightColor.blue; - elZoneKeyLightIntensity.value = properties.keyLightIntensity.toFixed(2); - elZoneKeyLightAmbientIntensity.value = properties.keyLightAmbientIntensity.toFixed(2); - elZoneKeyLightDirectionX.value = properties.keyLightDirection.x.toFixed(2); - elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2); - elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2); + elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; + elZoneKeyLightColorRed.value = properties.keyLight.color.red; + elZoneKeyLightColorGreen.value = properties.keyLight.color.green; + elZoneKeyLightColorBlue.value = properties.keyLight.color.blue; + elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2); + elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2); + elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2); + elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); + elZoneKeyLightDirectionZ.value = properties.keyLight.direction.z.toFixed(2); + elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL; + elZoneStageLatitude.value = properties.stage.latitude.toFixed(2); elZoneStageLongitude.value = properties.stage.longitude.toFixed(2); @@ -819,21 +823,20 @@ onSubmit: function(hsb, hex, rgb, el) { $(el).css('background-color', '#'+hex); $(el).colpickHide(); - emitColorPropertyUpdate('keyLightColor', rgb.r, rgb.g, rgb.b); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); } }); - var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction( - 'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); + var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); - elZoneKeyLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightIntensity')); - elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightAmbientIntensity')); - var zoneKeyLightDirectionChangeFunction = createEmitVec3PropertyUpdateFunction( - 'keyLightDirection', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ); + elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('intensity','keyLight')); + elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); + var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ); elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction); + elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL')); elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude')); elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude')); From 5acb088c4644d521fa9a66f28e2ecdacdbc4b271 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 28 Oct 2015 16:57:27 -0700 Subject: [PATCH 0503/1003] FBXReader: More deterministic loading behavior This makes iteration over meshes and connections between them deterministic. [QHash](http://doc.qt.io/qt-5/qhash.html#details) and QMultiHash do not guarantee consistent iteration order. This is problematic for the FBXReader because it could result in different behavior each time a model was loaded. Specifically, This was causing a bug with some avatars that contained multiple-bind poses. The bind pose returned to the application from the FBXReader would be different on different runs. This PR doesn't add support for multiple-bind poses, but it does make the choice of which bind pose is chosen deterministic. This non-determinism was the cause of the Mery avatar having "bug-eyes" 1/12 times. --- libraries/fbx/src/FBXReader.cpp | 115 ++++++++++++++++---------------- libraries/fbx/src/FBXReader.h | 4 +- 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1eda5304e4..d29733f6a3 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -64,7 +64,7 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { glm::vec3 minimum = glm::vec3(offset * glm::vec4(extents.minimum, 1.0f)); glm::vec3 maximum = glm::vec3(offset * glm::vec4(extents.maximum, 1.0f)); Extents scaledExtents = { minimum, maximum }; - + return scaledExtents; } @@ -73,7 +73,7 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const { if (!getUnscaledMeshExtents().containsPoint(point)) { return false; } - + auto checkEachPrimitive = [=](FBXMesh& mesh, QVector indices, int primitiveSize) -> bool { // Check whether the point is "behind" all the primitives. for (int j = 0; j < indices.size(); j += primitiveSize) { @@ -87,11 +87,11 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const { } return true; }; - + // Check that the point is contained in at least one convex mesh. for (auto mesh : meshes) { bool insideMesh = true; - + // To be considered inside a convex mesh, // the point needs to be "behind" all the primitives respective planes. for (auto part : mesh.parts) { @@ -108,7 +108,7 @@ bool FBXGeometry::convexHullContains(const glm::vec3& point) const { return true; } } - + // It wasn't in any mesh, return false. return false; } @@ -194,7 +194,7 @@ public: glm::vec3 rotationMax; // radians }; -glm::mat4 getGlobalTransform(const QMultiHash& _connectionParentMap, +glm::mat4 getGlobalTransform(const QMultiMap& _connectionParentMap, const QHash& models, QString nodeID, bool mixamoHack) { glm::mat4 globalTransform; while (!nodeID.isNull()) { @@ -228,12 +228,12 @@ void printNode(const FBXNode& node, int indentLevel) { int indentLength = 2; QByteArray spaces(indentLevel * indentLength, ' '); QDebug nodeDebug = qDebug(modelformat); - + nodeDebug.nospace() << spaces.data() << node.name.data() << ": "; foreach (const QVariant& property, node.properties) { nodeDebug << property; } - + foreach (const FBXNode& child, node.children) { printNode(child, indentLevel + 1); } @@ -246,7 +246,7 @@ public: glm::mat4 transformLink; }; -void appendModelIDs(const QString& parentID, const QMultiHash& connectionChildMap, +void appendModelIDs(const QString& parentID, const QMultiMap& connectionChildMap, QHash& models, QSet& remainingModels, QVector& modelIDs) { if (remainingModels.contains(parentID)) { modelIDs.append(parentID); @@ -331,7 +331,7 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList& connectionParentMap, +QString getTopModelID(const QMultiMap& connectionParentMap, const QHash& models, const QString& modelID) { QString topID = modelID; forever { @@ -342,7 +342,7 @@ QString getTopModelID(const QMultiHash& connectionParentMap, } } return topID; - + outerContinue: ; } } @@ -361,7 +361,7 @@ public: }; bool checkMaterialsHaveTextures(const QHash& materials, - const QHash& textureFilenames, const QMultiHash& _connectionChildMap) { + const QHash& textureFilenames, const QMultiMap& _connectionChildMap) { foreach (const QString& materialID, materials.keys()) { foreach (const QString& childID, _connectionChildMap.values(materialID)) { if (textureFilenames.contains(childID)) { @@ -443,8 +443,8 @@ QByteArray fileOnUrl(const QByteArray& filenameString, const QString& url) { } FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { - const FBXNode& node = _fbxNode; - QHash meshes; + const FBXNode& node = _fbxNode; + QMap meshes; QHash modelIDsToNames; QHash meshIDsToMeshIndices; QHash ooChildToParent; @@ -497,7 +497,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QVector humanIKJointIDs(humanIKJointNames.size()); QVariantHash blendshapeMappings = mapping.value("bs").toHash(); - + QMultiHash blendshapeIndices; for (int i = 0;; i++) { QByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i]; @@ -527,7 +527,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QString hifiGlobalNodeID; unsigned int meshIndex = 0; foreach (const FBXNode& child, node.children) { - + if (child.name == "FBXHeaderExtension") { foreach (const FBXNode& object, child.children) { if (object.name == "SceneInfo") { @@ -537,7 +537,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (subsubobject.name == "Author") { geometry.author = subsubobject.properties.at(0).toString(); } - } + } } else if (subobject.name == "Properties70") { foreach (const FBXNode& subsubobject, subobject.children) { if (subsubobject.name == "P" && subsubobject.properties.size() >= 5 && @@ -620,7 +620,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (humanIKJointIndex != -1) { humanIKJointIDs[humanIKJointIndex] = getID(object.properties); } - + glm::vec3 translation; // NOTE: the euler angles as supplied by the FBX file are in degrees glm::vec3 rotationOffset; @@ -709,7 +709,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // it's a mesh as well as a model mesh = &meshes[getID(object.properties)]; *mesh = extractMesh(object, meshIndex); - + } else if (subobject.name == "Shape") { ExtractedBlendshape blendshape = { subobject.properties.at(0).toString(), extractBlendshape(subobject) }; @@ -720,7 +720,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QString attributetype = subobject.properties.at(0).toString(); if (!attributetype.empty()) { if (attributetype == "Light") { - QString lightprop; + QString lightprop; foreach (const QVariant& vprop, subobject.properties) { lightprop = vprop.toString(); } @@ -731,23 +731,23 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } else { QString whatisthat = subobject.name; if (whatisthat == "Shape") { - } + } } #endif } - + // add the blendshapes included in the model, if any if (mesh) { foreach (const ExtractedBlendshape& extracted, blendshapes) { addBlendshapes(extracted, blendshapeIndices.values(extracted.id.toLatin1()), *mesh); } } - + // see FBX documentation, http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html model.translation = translation; - model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); - model.preRotation = glm::quat(glm::radians(preRotation)); + model.preTransform = glm::translate(rotationOffset) * glm::translate(rotationPivot); + model.preRotation = glm::quat(glm::radians(preRotation)); model.rotation = glm::quat(glm::radians(rotation)); model.postRotation = glm::quat(glm::radians(postRotation)); @@ -862,7 +862,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (subobject.name == "RelativeFilename") { filename = subobject.properties.at(0).toByteArray(); filename = fileOnUrl(filename, url); - + } else if (subobject.name == "Content" && !subobject.properties.isEmpty()) { content = subobject.properties.at(0).toByteArray(); } @@ -905,10 +905,10 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } else if (property.properties.at(0) == "Emissive") { material.emissiveColor = getVec3(property.properties, index); - + } else if (property.properties.at(0) == "Shininess") { material.shininess = property.properties.at(index).value(); - + } else if (property.properties.at(0) == "Opacity") { material.opacity = property.properties.at(index).value(); } @@ -1001,7 +1001,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } animationCurves.insert(getID(object.properties), curve); - + } #if defined(DEBUG_FBXREADER) else { @@ -1013,7 +1013,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } else { unknown++; } - } + } #endif } } else if (child.name == "Connections") { @@ -1041,14 +1041,14 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("transparentcolor")) { // it should be TransparentColor... - // THis is how Maya assign a texture that affect diffuse color AND transparency ? + // THis is how Maya assign a texture that affect diffuse color AND transparency ? diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("bump")) { bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("normal")) { normalTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("specular") || type.contains("reflection")) { - specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); + specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type == "lcl rotation") { localRotations.insert(getID(connection.properties, 2), getID(connection.properties, 1)); @@ -1097,7 +1097,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } else { unknown++; } - } + } #endif } @@ -1142,7 +1142,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } outerBreak: - + // make sure the parent is in the child map QString parent = _connectionParentMap.value(model.key()); if (!_connectionChildMap.contains(parent, model.key())) { @@ -1172,7 +1172,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS frame.translations.resize(modelIDs.size()); geometry.animationFrames.append(frame); } - + // convert the models to joints QVariantList freeJoints = mapping.values("freeJoint"); geometry.hasSkeletonJoints = false; @@ -1181,7 +1181,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS FBXJoint joint; joint.isFree = freeJoints.contains(model.name); joint.parentIndex = model.parentIndex; - + // get the indices of all ancestors starting with the first free one (if any) int jointIndex = geometry.joints.size(); joint.freeLineage.append(jointIndex); @@ -1203,7 +1203,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.rotationMax = model.rotationMax; glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; if (joint.parentIndex == -1) { - joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform * + joint.transform = geometry.offset * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; joint.inverseDefaultRotation = glm::inverse(combinedRotation); joint.distanceToParent = 0.0f; @@ -1272,11 +1272,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); geometry.leftToeJointIndex = modelIDs.indexOf(jointLeftToeID); geometry.rightToeJointIndex = modelIDs.indexOf(jointRightToeID); - + foreach (const QString& id, humanIKJointIDs) { geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); } - + // extract the translation component of the neck transform if (geometry.neckJointIndex != -1) { const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform; @@ -1285,7 +1285,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.bindExtents.reset(); geometry.meshExtents.reset(); - + // Create the Material Library consolidateFBXMaterials(); geometry.materials = _fbxMaterials; @@ -1293,9 +1293,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS // see if any materials have texture children bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, _textureFilenames, _connectionChildMap); - for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { + for (QMap::iterator it = meshes.begin(); it != meshes.end(); it++) { ExtractedMesh& extracted = it.value(); - + extracted.mesh.meshExtents.reset(); // accumulate local transforms @@ -1335,7 +1335,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } materialIndex++; - + } else if (_textureFilenames.contains(childID)) { FBXTexture texture = getTexture(childID); for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { @@ -1360,7 +1360,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS setTangents(extracted.mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3)); setTangents(extracted.mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i)); } - // <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0 + // <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0 // This is most likely evidence of a further problem in extractMesh() for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) { setTangents(extracted.mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1)); @@ -1401,6 +1401,11 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.bindTransform = cluster.transformLink; joint.bindTransformFoundInCluster = true; + if (fbxCluster.jointIndex == 63) { // Head + qCDebug(modelformat) << "AJT: Head found in cluster, id = " << clusterID; + qCDebug(modelformat) << "AJT: trans = " << extractTranslation(cluster.transformLink); + } + // update the bind pose extents glm::vec3 bindTranslation = extractTranslation(geometry.offset * joint.bindTransform); geometry.bindExtents.addPoint(bindTranslation); @@ -1500,7 +1505,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec4& weights = extracted.mesh.clusterWeights[i]; float total = weights.x + weights.y + weights.z + weights.w; if (total != 1.0f && total != 0.0f) { - weights /= total; + weights /= total; } } } else { @@ -1573,7 +1578,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS avgRadius += glm::length(offset - projection * axis); } avgRadius /= (float)points.size(); - + // compute endpoints of capsule in joint-frame glm::vec3 capsuleBegin = avgPoint; glm::vec3 capsuleEnd = avgPoint; @@ -1594,27 +1599,27 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS joint.shapeInfo.radius = avgRadius; } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); - + // Add sitting points QVariantHash sittingPoints = mapping.value("sit").toHash(); for (QVariantHash::const_iterator it = sittingPoints.constBegin(); it != sittingPoints.constEnd(); it++) { SittingPoint sittingPoint; sittingPoint.name = it.key(); - + QVariantList properties = it->toList(); sittingPoint.position = parseVec3(properties.at(0).toString()); sittingPoint.rotation = glm::quat(glm::radians(parseVec3(properties.at(1).toString()))); - + geometry.sittingPoints.append(sittingPoint); } - + // attempt to map any meshes to a named model - for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); + for (QHash::const_iterator m = meshIDsToMeshIndices.constBegin(); m != meshIDsToMeshIndices.constEnd(); m++) { - + const QString& meshID = m.key(); int meshIndex = m.value(); - + if (ooChildToParent.contains(meshID)) { const QString& modelID = ooChildToParent.value(meshID); if (modelIDsToNames.contains(modelID)) { @@ -1623,7 +1628,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } } - + return geometryPtr; } @@ -1641,5 +1646,3 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri return reader.extractFBXGeometry(mapping, url); } - - diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 0af7b28136..8014718815 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -413,8 +413,8 @@ public: float _lightmapOffset = 0.0f; float _lightmapLevel; - QMultiHash _connectionParentMap; - QMultiHash _connectionChildMap; + QMultiMap _connectionParentMap; + QMultiMap _connectionChildMap; static glm::vec3 getVec3(const QVariantList& properties, int index); static QVector createVec4Vector(const QVector& doubleVector); From 136747929e76cf2e449b13f3e29e3bab75b78bba Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 28 Oct 2015 17:11:46 -0700 Subject: [PATCH 0504/1003] Fix typo --- libraries/script-engine/src/ScriptEngine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 39f0963112..b416b58910 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -96,7 +96,7 @@ static bool hasCorrectSyntax(const QScriptProgram& program) { return true; } -static bool hadUncauchtExceptions(QScriptEngine& engine, const QString& fileName) { +static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) { if (engine.hasUncaughtException()) { const auto backtrace = engine.uncaughtExceptionBacktrace(); const auto exception = engine.uncaughtException().toString(); @@ -616,7 +616,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi const auto result = QScriptEngine::evaluate(program); --_evaluatesPending; - const auto hadUncaughtException = hadUncauchtExceptions(*this, program.fileName()); + const auto hadUncaughtException = hadUncaughtExceptions(*this, program.fileName()); if (_wantSignals) { emit evaluationFinished(result, hadUncaughtException); } @@ -686,7 +686,7 @@ void ScriptEngine::run() { lastUpdate = now; // Debug and clear exceptions - hadUncauchtExceptions(*this, _fileNameString); + hadUncaughtExceptions(*this, _fileNameString); } stopAllTimers(); // make sure all our timers are stopped if the script is ending @@ -1021,7 +1021,7 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co QScriptEngine sandbox; QScriptValue testConstructor = sandbox.evaluate(program); - if (hadUncauchtExceptions(sandbox, program.fileName())) { + if (hadUncaughtExceptions(sandbox, program.fileName())) { return; } From 4cb5ccab90f3df912f2091c167edf9a79881c4ce Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 29 Oct 2015 13:18:27 +1300 Subject: [PATCH 0505/1003] Fix audio stats dialog not being sized properly --- interface/src/ui/AudioStatsDialog.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/AudioStatsDialog.cpp b/interface/src/ui/AudioStatsDialog.cpp index e57182e251..a475700e02 100644 --- a/interface/src/ui/AudioStatsDialog.cpp +++ b/interface/src/ui/AudioStatsDialog.cpp @@ -65,11 +65,12 @@ AudioStatsDialog::AudioStatsDialog(QWidget* parent) : // Get statistics from the Audio Client _stats = &DependencyManager::get()->getStats(); - // Create layouter + // Create layout _form = new QFormLayout(); + _form->setSizeConstraint(QLayout::SetFixedSize); QDialog::setLayout(_form); - // Load and initilize all channels + // Load and initialize all channels renderStats(); _audioDisplayChannels = QVector>(1); @@ -80,10 +81,8 @@ AudioStatsDialog::AudioStatsDialog(QWidget* parent) : _downstreamID = addChannel(_form, _downstreamStats, COLOR3); _upstreamInjectedID = addChannel(_form, _upstreamInjectedStats, COLOR0); - connect(averageUpdateTimer, SIGNAL(timeout()), this, SLOT(updateTimerTimeout())); averageUpdateTimer->start(1000); - } int AudioStatsDialog::addChannel(QFormLayout* form, QVector& stats, const unsigned color) { @@ -243,7 +242,6 @@ void AudioStatsDialog::paintEvent(QPaintEvent* event) { } QDialog::paintEvent(event); - setFixedSize(width(), height()); } void AudioStatsDialog::reject() { From 568e5536b8e8f3b3554b6476bfd886389778be95 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 29 Oct 2015 13:18:43 +1300 Subject: [PATCH 0506/1003] Control bandwidth stats dialog sizing similarly --- interface/src/ui/BandwidthDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/BandwidthDialog.cpp b/interface/src/ui/BandwidthDialog.cpp index e086005783..f07c844894 100644 --- a/interface/src/ui/BandwidthDialog.cpp +++ b/interface/src/ui/BandwidthDialog.cpp @@ -72,8 +72,9 @@ BandwidthDialog::BandwidthDialog(QWidget* parent) : this->setWindowTitle("Bandwidth Details"); - // Create layouter + // Create layout QFormLayout* form = new QFormLayout(); + form->setSizeConstraint(QLayout::SetFixedSize); this->QDialog::setLayout(form); QSharedPointer bandwidthRecorder = DependencyManager::get(); @@ -118,7 +119,6 @@ void BandwidthDialog::paintEvent(QPaintEvent* event) { for (unsigned int i=0; i<_CHANNELCOUNT; i++) _allChannelDisplays[i]->paint(); this->QDialog::paintEvent(event); - this->setFixedSize(this->width(), this->height()); } void BandwidthDialog::reject() { From 630cdb8a9dd38e4d2ca9cacbf727101ecd5d3625 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 28 Oct 2015 17:29:06 -0700 Subject: [PATCH 0507/1003] Modify the html presentiong the zone enity properties to match the internal new representation --- examples/html/entityProperties.html | 61 ++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index a8eff20df7..b3d7a5f6b6 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -1479,8 +1479,12 @@
-
-
Key Light Color
+
+ +
+ +
+
Light Color
R
@@ -1488,20 +1492,16 @@
B
-
-
Key Light Intensity
+ +
+
Light Intensity
-
-
Key Light Ambient Intensity
-
- -
-
-
-
Key Light Direction
+ +
+
Light Direction
Pitch
Yaw
@@ -1509,46 +1509,69 @@
-
+
+
Ambient Intensity
+
+ +
+
+ + +
+
Ambient URL
+
+ +
+
+ +
+ +
+ +
Stage Latitude
-
+
Stage Longitude
-
+
Stage Altitude
-
+
Automatically calculate stage hour and day from location and clock.
-
+
Stage Day
-
+
Stage Hour
-
+
+ +
+ +
Background Mode
; 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 0514/1003] 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 025422792d134fefd4fe65a4815f6c5197849f4e Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 10:13:28 -0700 Subject: [PATCH 0515/1003] Fixing the keyLIght Direction ui for edit.js --- examples/edit.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 09d30c712b..d1f6edb47a 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1510,9 +1510,9 @@ PropertiesTool = function(opts) { if (entity.properties.rotation !== undefined) { entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); } - if (entity.properties.keyLightDirection !== undefined) { - entity.properties.keyLightDirection = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLightDirection)); - entity.properties.keyLightDirection.z = 0.0; + if (entity.properties.keyLight.Direction !== undefined) { + entity.properties.keyLight.Direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.Direction)); + entity.properties.keyLight.Direction.z = 0.0; } selections.push(entity); } @@ -1541,9 +1541,9 @@ PropertiesTool = function(opts) { var rotation = data.properties.rotation; data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); } - if (data.properties.keyLightDirection !== undefined) { - data.properties.keyLightDirection = Vec3.fromPolar( - data.properties.keyLightDirection.x * DEGREES_TO_RADIANS, data.properties.keyLightDirection.y * DEGREES_TO_RADIANS); + if (data.properties.keyLight.Direction !== undefined) { + data.properties.keyLight.Direction = Vec3.fromPolar( + data.properties.keyLight.Direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.Direction.y * DEGREES_TO_RADIANS); } Entities.editEntity(selectionManager.selections[0], data.properties); if (data.properties.name != undefined) { From 49877c2dffba35b6902e3c92dfd9568615dcab78 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 29 Oct 2015 10:19:18 -0700 Subject: [PATCH 0516/1003] Move _hasOutgoingChanges in onSubStep to previous location --- libraries/physics/src/PhysicsEngine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 1e87fc9a5d..7af1c12917 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -253,7 +253,6 @@ void PhysicsEngine::stepSimulation() { auto onSubStep = [this]() { updateContactMap(); - _hasOutgoingChanges = true; }; int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); @@ -265,6 +264,8 @@ void PhysicsEngine::stepSimulation() { if (_characterController) { _characterController->postSimulation(); } + + _hasOutgoingChanges = true; } } From 4083c5c71b848d300ef73709003a119e06dbe4a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 10:31:36 -0700 Subject: [PATCH 0517/1003] 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 517387621a5c0b5389d240bb8846dbef7bd0421c Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 10:40:28 -0700 Subject: [PATCH 0518/1003] Fixing the keyLIght Direction ui for edit.js and fix the case for D to d --- examples/edit.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index d1f6edb47a..c45e66986a 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1510,9 +1510,9 @@ PropertiesTool = function(opts) { if (entity.properties.rotation !== undefined) { entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); } - if (entity.properties.keyLight.Direction !== undefined) { - entity.properties.keyLight.Direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.Direction)); - entity.properties.keyLight.Direction.z = 0.0; + if (entity.properties.keyLight.direction !== undefined) { + entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.direction)); + entity.properties.keyLight.direction.z = 0.0; } selections.push(entity); } @@ -1541,9 +1541,9 @@ PropertiesTool = function(opts) { var rotation = data.properties.rotation; data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); } - if (data.properties.keyLight.Direction !== undefined) { - data.properties.keyLight.Direction = Vec3.fromPolar( - data.properties.keyLight.Direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.Direction.y * DEGREES_TO_RADIANS); + if (data.properties.keyLight.direction !== undefined) { + data.properties.keyLight.direction = Vec3.fromPolar( + data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.direction.y * DEGREES_TO_RADIANS); } Entities.editEntity(selectionManager.selections[0], data.properties); if (data.properties.name != undefined) { From ed87ae3d5a4461dacdef532188af3763293c205e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 10:57:30 -0700 Subject: [PATCH 0519/1003] 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 0520/1003] 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 0521/1003] 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 0522/1003] 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 0523/1003] 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 143b9c663f2e6cb530133aadf19e6f27d49ebe2d Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Oct 2015 12:21:28 -0700 Subject: [PATCH 0524/1003] Quick fix for audio having channels swapped under Qt 5.5.1. --- libraries/audio-client/src/AudioClient.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index b528b67745..c321a32211 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -313,7 +313,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, const QAudioFormat& desiredAudioFormat, QAudioFormat& adjustedAudioFormat) { - if (!audioDevice.isFormatSupported(desiredAudioFormat)) { + // FIXME: direcly using 24khz has a bug somewhere that causes channels to be swapped. + // Continue using our internal resampler, for now. + if (false && !audioDevice.isFormatSupported(desiredAudioFormat)) { qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; qCDebug(audioclient, "The desired audio format is not supported by this device"); From 4d576d7aaff7284d4d5d10640d1ab97c27b4405c Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Oct 2015 12:23:36 -0700 Subject: [PATCH 0525/1003] Fix typo --- libraries/audio-client/src/AudioClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c321a32211..a67ab460d1 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -313,7 +313,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, const QAudioFormat& desiredAudioFormat, QAudioFormat& adjustedAudioFormat) { - // FIXME: direcly using 24khz has a bug somewhere that causes channels to be swapped. + // FIXME: directly using 24khz has a bug somewhere that causes channels to be swapped. // Continue using our internal resampler, for now. if (false && !audioDevice.isFormatSupported(desiredAudioFormat)) { qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; From 1c2973f17d9b292c699a5899385d392ae8cb779a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 12:34:24 -0700 Subject: [PATCH 0526/1003] removed old metavoxel example, since the api is no longer available --- examples/example/metavoxels/metavoxels.js | 41 ----------------------- 1 file changed, 41 deletions(-) delete mode 100644 examples/example/metavoxels/metavoxels.js diff --git a/examples/example/metavoxels/metavoxels.js b/examples/example/metavoxels/metavoxels.js deleted file mode 100644 index 32177cdcba..0000000000 --- a/examples/example/metavoxels/metavoxels.js +++ /dev/null @@ -1,41 +0,0 @@ -// -// metavoxels.js -// examples -// -// 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 -// - -Script.setInterval(function() { - var spanner; - if (Math.random() < 0.5) { - spanner = new Sphere(); - } else { - spanner = new Cuboid(); - spanner.aspectX = 0.1 + Math.random() * 1.9; - spanner.aspectY = 0.1 + Math.random() * 1.9; - } - spanner.scale = 0.1 + Math.random() * 1.9; - spanner.rotation = Quat.fromPitchYawRollDegrees(Math.random() * 360.0, Math.random() * 360.0, Math.random() * 360.0); - spanner.translation = { x: 10.0 + Math.random() * 10.0, y: 10.0 + Math.random() * 10.0, z: 10.0 + Math.random() * 10.0 }; - - if (Math.random() < 0.5) { - var material = new MaterialObject(); - if (Math.random() < 0.5) { - material.diffuse = "http://www.fungibleinsight.com/faces/grass.jpg"; - } else { - material.diffuse = "http://www.fungibleinsight.com/faces/soil.jpg"; - } - Metavoxels.setVoxelMaterial(spanner, material); - - } else if (Math.random() < 0.5) { - Metavoxels.setVoxelColor(spanner, { red: Math.random() * 255.0, green: Math.random() * 255.0, - blue: Math.random() * 255.0 }); - - } else { - Metavoxels.setVoxelColor(spanner, { red: 0, green: 0, blue: 0, alpha: 0 }); - } -}, 1000); - From fb55f0becbbb1a0ae589224f4f26906d939ed191 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 29 Oct 2015 12:50:34 -0700 Subject: [PATCH 0527/1003] guard timeElapsed for simulateKinematicMotion to max 1s --- libraries/entities/src/EntityItem.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f012ba6eee..cce3045049 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -736,18 +736,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // is sending us data with a known "last simulated" time. That time is likely in the past, and therefore // this "new" data is actually slightly out of date. We calculate the time we need to skip forward and // use our simulation helper routine to get a best estimate of where the entity should be. - const float MIN_TIME_SKIP = 0.0f; - const float MAX_TIME_SKIP = 1.0f; // in seconds - float skipTimeForward = glm::clamp((float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND), - MIN_TIME_SKIP, MAX_TIME_SKIP); - if (skipTimeForward > 0.0f) { - #ifdef WANT_DEBUG - qCDebug(entities) << "skipTimeForward:" << skipTimeForward; - #endif - // we want to extrapolate the motion forward to compensate for packet travel time, but - // we don't want the side effect of flag setting. - simulateKinematicMotion(skipTimeForward, false); - } + float skipTimeForward = (float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND); + + // we want to extrapolate the motion forward to compensate for packet travel time, but + // we don't want the side effect of flag setting. + simulateKinematicMotion(skipTimeForward, false); } if (overwriteLocalData) { @@ -887,6 +880,15 @@ void EntityItem::simulate(const quint64& now) { } void EntityItem::simulateKinematicMotion(float timeElapsed, bool setFlags) { +#ifdef WANT_DEBUG + qCDebug(entities) << "EntityItem::simulateKinematicMotion timeElapsed" << timeElapsed; +#endif + + const float MIN_TIME_SKIP = 0.0f; + const float MAX_TIME_SKIP = 1.0f; // in seconds + + timeElapsed = glm::clamp(timeElapsed, MIN_TIME_SKIP, MAX_TIME_SKIP); + if (hasActions()) { return; } From 1eabb924f1ce844332bc9e7f001bf50a6bc9fb27 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 29 Oct 2015 12:53:47 -0700 Subject: [PATCH 0528/1003] Fixed the logic to be correct --- libraries/audio-client/src/AudioClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index a67ab460d1..d4980596dd 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -315,7 +315,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& adjustedAudioFormat) { // FIXME: directly using 24khz has a bug somewhere that causes channels to be swapped. // Continue using our internal resampler, for now. - if (false && !audioDevice.isFormatSupported(desiredAudioFormat)) { + if (true || !audioDevice.isFormatSupported(desiredAudioFormat)) { qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; qCDebug(audioclient, "The desired audio format is not supported by this device"); @@ -323,7 +323,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat = desiredAudioFormat; adjustedAudioFormat.setChannelCount(2); - if (audioDevice.isFormatSupported(adjustedAudioFormat)) { + if (false && audioDevice.isFormatSupported(adjustedAudioFormat)) { return true; } else { adjustedAudioFormat.setChannelCount(1); From e902e5e97a725f569ab7cc344373c8197b5d8ca2 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 13:05:29 -0700 Subject: [PATCH 0529/1003] 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 0530/1003] 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 0531/1003] 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 904326cd1c15911a307b5beebb382afdd13578fe Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Oct 2015 14:11:27 -0700 Subject: [PATCH 0532/1003] edit.js fix --- examples/html/entityProperties.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index b3d7a5f6b6..fe40311b5f 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -1520,7 +1520,7 @@
Ambient URL
- +
From 020566b76a996c931aece545e98782124f329397 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 29 Oct 2015 14:14:15 -0700 Subject: [PATCH 0533/1003] no deleting --- examples/painting/whiteboard/whiteboardEntityScript.js | 2 +- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index ce0e8a82b1..c10a8c23fe 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -243,7 +243,7 @@ unload: function() { Overlays.deleteOverlay(this.laserPointer); - this.eraseBoard(); + // this.eraseBoard(); } }; diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 29183dcc76..b2156dc898 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -209,4 +209,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -Script.scriptEnding.connect(cleanup); \ No newline at end of file +// Script.scriptEnding.connect(cleanup); \ No newline at end of file From f72146c35d224bffedea7a6d28195966173d14a1 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 29 Oct 2015 14:17:52 -0700 Subject: [PATCH 0534/1003] 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 0535/1003] 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 c237072195288e18c8c643c07c35d3e5a5544c72 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Oct 2015 15:29:12 -0700 Subject: [PATCH 0536/1003] edit.js fix --- examples/edit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index c45e66986a..447455e999 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1510,7 +1510,7 @@ PropertiesTool = function(opts) { if (entity.properties.rotation !== undefined) { entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); } - if (entity.properties.keyLight.direction !== undefined) { + if (entity.properties.type === "Zone" && entity.properties.keyLight.direction !== undefined) { entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.direction)); entity.properties.keyLight.direction.z = 0.0; } @@ -1541,7 +1541,7 @@ PropertiesTool = function(opts) { var rotation = data.properties.rotation; data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); } - if (data.properties.keyLight.direction !== undefined) { + if (entity.properties.type === "Zone" && data.properties.keyLight.direction !== undefined) { data.properties.keyLight.direction = Vec3.fromPolar( data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.direction.y * DEGREES_TO_RADIANS); } From b070306c2c002bd08662fcc4c7c3b860bd5fc4cc Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 15:53:17 -0700 Subject: [PATCH 0537/1003] 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 047614b2eb70fc7e60faef1486de3b515078344b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 29 Oct 2015 16:18:15 -0700 Subject: [PATCH 0538/1003] New left and right grab animations. --- .../defaultAvatar_full/avatar-animation.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 682e0be1bf..515f6e96fe 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -197,7 +197,7 @@ "id": "rightHandOpen", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -209,9 +209,9 @@ "id": "rightHandClose", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/squeeze_hands/right_hand_anim.fbx", - "startFrame": 15.0, - "endFrame": 15.0, + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx", + "startFrame": 10.0, + "endFrame": 10.0, "timeScale": 1.0, "loopFlag": true }, @@ -346,7 +346,7 @@ "id": "leftHandOpen", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -358,9 +358,9 @@ "id": "leftHandClose", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/squeeze_hands/left_hand_anim.fbx", - "startFrame": 15.0, - "endFrame": 15.0, + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx", + "startFrame": 10.0, + "endFrame": 10.0, "timeScale": 1.0, "loopFlag": true }, From 303491817bd14dd17e2ab6eaa95674145c125bca Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 16:35:59 -0700 Subject: [PATCH 0539/1003] 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 b6b71e7e7693aa92962ff080f562909e82238dec Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 29 Oct 2015 16:40:50 -0700 Subject: [PATCH 0540/1003] increased range and intensity of light --- examples/painting/whiteboard/whiteboardSpawner.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index b2156dc898..fa5040ed4b 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -79,11 +79,13 @@ var drawingSurface = Entities.addEntity({ }); -var lightPosition = Vec3.sum(center, Vec3.multiply(-2, Quat.getFront(rotation))); +var lightPosition = Vec3.sum(center, Vec3.multiply(-4, Quat.getFront(rotation))); var light = Entities.addEntity({ type: 'Light', + name: 'whiteboard light', position: lightPosition, - dimensions: {x: 5, y: 5, z: 5}, + dimensions: {x: 10, y: 10, z: 10}, + intensity: 2, color: {red: 255, green: 255, blue: 255} }); From 8b46ed7dff4e54bf9817bf0ee3df69ba62997bb2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 29 Oct 2015 16:42:48 -0700 Subject: [PATCH 0541/1003] Check keyLight is defined --- examples/edit.js | 4 ++-- examples/html/entityProperties.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index 447455e999..7a16030afc 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1510,7 +1510,7 @@ PropertiesTool = function(opts) { if (entity.properties.rotation !== undefined) { entity.properties.rotation = Quat.safeEulerAngles(entity.properties.rotation); } - if (entity.properties.type === "Zone" && entity.properties.keyLight.direction !== undefined) { + if (entity.properties.keyLight !== undefined && entity.properties.keyLight.direction !== undefined) { entity.properties.keyLight.direction = Vec3.multiply(RADIANS_TO_DEGREES, Vec3.toPolar(entity.properties.keyLight.direction)); entity.properties.keyLight.direction.z = 0.0; } @@ -1541,7 +1541,7 @@ PropertiesTool = function(opts) { var rotation = data.properties.rotation; data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z); } - if (entity.properties.type === "Zone" && data.properties.keyLight.direction !== undefined) { + if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) { data.properties.keyLight.direction = Vec3.fromPolar( data.properties.keyLight.direction.x * DEGREES_TO_RADIANS, data.properties.keyLight.direction.y * DEGREES_TO_RADIANS); } diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index fe40311b5f..412b413b2b 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -830,7 +830,7 @@ elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); - elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('intensity','keyLight')); + elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity')); elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ); elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); From 502cc7f580c90483b65d10638f2e622a40c8c702 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 16:44:01 -0700 Subject: [PATCH 0542/1003] 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 6bdf66534f323d1710878bcc5f6d48f32e9a1992 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 29 Oct 2015 16:53:03 -0700 Subject: [PATCH 0543/1003] light tweak --- examples/painting/whiteboard/whiteboardSpawner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index fa5040ed4b..701112e1a8 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -79,7 +79,7 @@ var drawingSurface = Entities.addEntity({ }); -var lightPosition = Vec3.sum(center, Vec3.multiply(-4, Quat.getFront(rotation))); +var lightPosition = Vec3.sum(center, Vec3.multiply(-3, Quat.getFront(rotation))); var light = Entities.addEntity({ type: 'Light', name: 'whiteboard light', From 35e03d662d392af02b95969138b3d5cc84cdba37 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 30 Oct 2015 00:58:32 +0100 Subject: [PATCH 0544/1003] fix the constants of Vec3 , const glm::vec3& UNIT_***() weren't accessible in javascript. Solved it by making them properties that are CONSTANT. Also added function to multiply a vec3 by a vec3 called multiplyVbyV since overloading it over multiply gave me some problems --- libraries/script-engine/src/Vec3.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index b05e729a49..17a5afd09a 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -23,6 +23,25 @@ /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API class Vec3 : public QObject { Q_OBJECT + Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_Z READ UNIT_Z CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_NEG_X READ UNIT_NEG_X CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_NEG_Y READ UNIT_NEG_Y CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_NEG_Z READ UNIT_NEG_Z CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_XY READ UNIT_XY CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_XZ READ UNIT_XZ CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_YZ READ UNIT_YZ CONSTANT) + Q_PROPERTY(glm::vec3 UNIT_XYZ READ UNIT_XYZ CONSTANT) + Q_PROPERTY(glm::vec3 FLOAT_MAX READ FLOAT_MAX CONSTANT) + Q_PROPERTY(glm::vec3 FLOAT_MIN READ FLOAT_MIN CONSTANT) + Q_PROPERTY(glm::vec3 ZERO READ ZERO CONSTANT) + Q_PROPERTY(glm::vec3 ONE READ ONE CONSTANT) + Q_PROPERTY(glm::vec3 TWO READ TWO CONSTANT) + Q_PROPERTY(glm::vec3 HALF READ HALF CONSTANT) + Q_PROPERTY(glm::vec3 RIGHT READ RIGHT CONSTANT) + Q_PROPERTY(glm::vec3 UP READ UP CONSTANT) + Q_PROPERTY(glm::vec3 FRONT READ FRONT CONSTANT) public slots: glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } @@ -30,6 +49,7 @@ public slots: float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } + glm::vec3 multiplyVbyV(const glm::vec3& v1, const glm::vec3& v2) { return v1 * v2; } glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } @@ -45,6 +65,8 @@ public slots: glm::vec3 toPolar(const glm::vec3& v); glm::vec3 fromPolar(const glm::vec3& polar); glm::vec3 fromPolar(float elevation, float azimuth); + +private: const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } From b56c49a182154c66dd20720825e0ea55e7d3713f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 17:33:30 -0700 Subject: [PATCH 0545/1003] 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 0546/1003] 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 0547/1003] 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 0548/1003] 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 0549/1003] 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 0550/1003] 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 0551/1003] 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 0552/1003] 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 0553/1003] 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 792fed0090a7f633e92e7912d0297b8d9134e4be Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 29 Oct 2015 22:15:26 -0700 Subject: [PATCH 0554/1003] We weren't setting type for avatar motion states. --- interface/src/avatar/AvatarMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index cabe545f5a..acd9a45aab 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -18,6 +18,7 @@ AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); + _type = MOTIONSTATE_TYPE_AVATAR; if (_shape) { _mass = 100.0f; // HACK } From 8a03383adc237f77105edb984d48a036b82584af Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 29 Oct 2015 22:19:33 -0700 Subject: [PATCH 0555/1003] 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 0556/1003] 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 a149a2dd60bcf1abfeb85f80ee02dea3d220c635 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 00:19:14 -0700 Subject: [PATCH 0557/1003] fix some warnings --- libraries/gl/src/gl/GlWindow.cpp | 3 +-- libraries/physics/src/CharacterController.cpp | 4 ++-- libraries/ui/src/VrMenu.cpp | 4 ++++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/gl/src/gl/GlWindow.cpp b/libraries/gl/src/gl/GlWindow.cpp index 7e43b29e73..40a5bedf7e 100644 --- a/libraries/gl/src/gl/GlWindow.cpp +++ b/libraries/gl/src/gl/GlWindow.cpp @@ -44,8 +44,7 @@ bool GlWindow::makeCurrent() { qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); }); - QOpenGLContext * currentContext = QOpenGLContext::currentContext(); - Q_ASSERT(_context == currentContext); + Q_ASSERT(_context == QOpenGLContext::currentContext()); return makeCurrentResult; } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index aadf9b16ea..8da9541387 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -14,11 +14,11 @@ #include "PhysicsCollisionGroups.h" bool CharacterController::needsRemoval() const { - return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); + return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION); } bool CharacterController::needsAddition() const { - return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); + return ((_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION); } void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 57df4d5306..211e0e0f72 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -113,6 +113,7 @@ void VrMenu::addMenu(QMenu* menu) { Q_ARG(QVariant, QVariant::fromValue(qmlParent)), Q_ARG(QVariant, QVariant::fromValue(menu->title()))); Q_ASSERT(invokeResult); + Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x QObject* result = returnedValue.value(); Q_ASSERT(result); @@ -153,6 +154,7 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { Q_ARG(QVariant, QVariant::fromValue(menuQml)), Q_ARG(QVariant, QVariant::fromValue(action->text()))); Q_ASSERT(invokeResult); + Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x QObject* result = returnedValue.value(); Q_ASSERT(result); // Bind the QML and Widget together @@ -174,6 +176,7 @@ void VrMenu::insertAction(QAction* before, QAction* action) { Q_ARG(QVariant, QVariant::fromValue(beforeQml)), Q_ARG(QVariant, QVariant::fromValue(action->text()))); Q_ASSERT(invokeResult); + Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x QObject* result = returnedValue.value(); Q_ASSERT(result); bindActionToQmlAction(result, action); @@ -192,4 +195,5 @@ void VrMenu::removeAction(QAction* action) { Q_ARG(QVariant, QVariant::fromValue(menu)), Q_ARG(QVariant, QVariant::fromValue(item))); Q_ASSERT(invokeResult); + Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x } From fcfab0fc513a96ae166318f613581cf083916e6e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 29 Oct 2015 12:31:34 -0700 Subject: [PATCH 0558/1003] Moving SDL2, Sixense and Faceshift to external projects --- CMakeLists.txt | 25 ++++----- cmake/externals/faceshift/CMakeLists.txt | 30 +++++++++++ cmake/externals/sdl2/CMakeLists.txt | 30 ++++------- cmake/externals/sixense/CMakeLists.txt | 36 ++++--------- .../AddDependencyExternalProjects.cmake | 2 +- cmake/macros/TargetFaceshift.cmake | 14 +++++ cmake/macros/TargetSDL2.cmake | 14 +++++ cmake/macros/TargetSixense.cmake | 14 +++++ cmake/modules/FindFaceshift.cmake | 23 -------- cmake/modules/FindSixense.cmake | 45 +--------------- interface/CMakeLists.txt | 13 +++-- interface/external/faceshift/readme.txt | 26 --------- interface/external/sdl2/readme.txt | 13 ----- interface/external/sixense/readme.txt | 10 ---- libraries/input-plugins/CMakeLists.txt | 54 +------------------ 15 files changed, 119 insertions(+), 230 deletions(-) create mode 100644 cmake/externals/faceshift/CMakeLists.txt create mode 100644 cmake/macros/TargetFaceshift.cmake create mode 100644 cmake/macros/TargetSDL2.cmake create mode 100644 cmake/macros/TargetSixense.cmake delete mode 100644 interface/external/faceshift/readme.txt delete mode 100644 interface/external/sdl2/readme.txt delete mode 100644 interface/external/sixense/readme.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 79d8a0c0e0..d69b8c4c77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,21 +186,22 @@ set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) setup_externals_binary_dir() # setup options to grab external project dependencies -option(GET_BULLET "Get Bullet library automatically as external project" 1) -option(GET_GLM "Get GLM library automatically as external project" 1) -option(GET_GVERB "Get Gverb library automatically as external project" 1) -option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) -option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) -option(GET_VHACD "Get V-HACD library automatically as external project" 1) -option(GET_POLYVOX "Get polyvox library automatically as external project" 1) -option(GET_OPENVR "Get OpenVR library automatically as external project" 1) -option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) -option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) -option(GET_GLEW "Get GLEW library automatically as external project" 1) +#option(GET_BULLET "Get Bullet library automatically as external project" 1) +#option(GET_GLM "Get GLM library automatically as external project" 1) +#option(GET_GVERB "Get Gverb library automatically as external project" 1) +#option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) +#option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) +#option(GET_VHACD "Get V-HACD library automatically as external project" 1) +#option(GET_POLYVOX "Get polyvox library automatically as external project" 1) +#option(GET_OPENVR "Get OpenVR library automatically as external project" 1) +#option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) +#option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) +#option(GET_GLEW "Get GLEW library automatically as external project" 1) +#option(GET_SDL2 "Get SDL2 library automatically as external project" 1) +#option(GET_SIXENSE "Get Sixense library automatically as external project" 1) option(USE_NSIGHT "Attempt to find the nSight libraries" 1) -option(GET_SDL2 "Get SDL2 library automatically as external project" 0) if (WIN32) add_paths_to_fixup_libs("${QT_DIR}/bin") diff --git a/cmake/externals/faceshift/CMakeLists.txt b/cmake/externals/faceshift/CMakeLists.txt new file mode 100644 index 0000000000..b18b861912 --- /dev/null +++ b/cmake/externals/faceshift/CMakeLists.txt @@ -0,0 +1,30 @@ +set(EXTERNAL_NAME faceshift) + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/dependencies/faceshift.zip + CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= + BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# URL_MD5 1bdcb8a0b8d5b1ede434cc41efade41d + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory") + +if (WIN32) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/faceshift.lib CACHE FILEPATH "Faceshift libraries") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/faceshift.lib CACHE FILEPATH "Faceshift libraries") +elseif (APPLE) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libfaceshift.a CACHE FILEPATH "Faceshift libraries") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libfaceshift.a CACHE FILEPATH "Faceshift libraries") +endif() diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index e6c80cf6ef..0f44f28610 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -47,25 +47,13 @@ if (APPLE) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory") set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library") +elseif (WIN32) + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") else () - if (WIN32) - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - set(_ROOT_DIR ${SOURCE_DIR}) - set(_INCLUDE_DIR ${_ROOT_DIR}/include) - set(_LIB_DIR "${SOURCE_DIR}/lib/x86") - set(_LIB_EXT "lib") - - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of SDL2 DLL") - else () - ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - set(_ROOT_DIR ${INSTALL_DIR}) - set(_INCLUDE_DIR ${_ROOT_DIR}/include/SDL2) - - set(_LIB_DIR ${INSTALL_DIR}/lib) - set(_LIB_EXT "so") - set(_LIB_PREFIX "lib") - endif () - - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${_INCLUDE_DIR} CACHE PATH "Location of SDL2 include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${_LIB_DIR}/${_LIB_PREFIX}SDL2.${_LIB_EXT} CACHE FILEPATH "Path to SDL2 library") -endif () \ No newline at end of file + ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library") +endif () diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index f6646e2272..72de4a7e15 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -1,30 +1,20 @@ include(ExternalProject) -include(SelectLibraryConfigurations) -set(EXTERNAL_NAME Sixense) +set(EXTERNAL_NAME sixense) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL ./SixenseSDK_062612.zip - URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc + URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip + URL_MD5 752a3901f334124e9cffc2ba4136ef7d CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" LOG_DOWNLOAD 1 ) -if (APPLE) - find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) -elseif (UNIX) - find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) - # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) -elseif (WIN32) -endif () - - +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) @@ -39,22 +29,18 @@ if (WIN32) set(ARCH_DIR "Win32") set(ARCH_SUFFIX "") endif() - - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) + + set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) + add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll") elseif(APPLE) - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32) + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) elseif(NOT ANDROID) - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32) - + # FIXME need to account for different architectures + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) + endif() diff --git a/cmake/macros/AddDependencyExternalProjects.cmake b/cmake/macros/AddDependencyExternalProjects.cmake index e859ef2db5..e8292c3198 100644 --- a/cmake/macros/AddDependencyExternalProjects.cmake +++ b/cmake/macros/AddDependencyExternalProjects.cmake @@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER) # has the user told us they specific don't want this as an external project? - if (GET_${_PROJ_NAME_UPPER}) + if (NOT DISABLE_${_PROJ_NAME_UPPER}) # have we already detected we can't have this as external project on this OS? if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT) # have we already setup the target? diff --git a/cmake/macros/TargetFaceshift.cmake b/cmake/macros/TargetFaceshift.cmake new file mode 100644 index 0000000000..99f65d942a --- /dev/null +++ b/cmake/macros/TargetFaceshift.cmake @@ -0,0 +1,14 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_FACESHIFT) + add_dependency_external_projects(faceshift) + find_package(Faceshift REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${FACESHIFT_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES}) + add_definitions(-DHAVE_FACESHIFT) +endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetSDL2.cmake b/cmake/macros/TargetSDL2.cmake new file mode 100644 index 0000000000..ee2328dfff --- /dev/null +++ b/cmake/macros/TargetSDL2.cmake @@ -0,0 +1,14 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_SDL2) + add_dependency_external_projects(sdl2) + find_package(SDL2 REQUIRED) + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SDL2_INCLUDE_DIR}) + target_link_libraries(${TARGET_NAME} ${SDL2_LIBRARY}) + add_definitions(-DHAVE_SDL2) +endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetSixense.cmake b/cmake/macros/TargetSixense.cmake new file mode 100644 index 0000000000..b52af9cdd2 --- /dev/null +++ b/cmake/macros/TargetSixense.cmake @@ -0,0 +1,14 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_SIXENSE) + add_dependency_external_projects(sixense) + find_package(Sixense REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) + add_definitions(-DHAVE_SIXENSE) +endmacro() \ No newline at end of file diff --git a/cmake/modules/FindFaceshift.cmake b/cmake/modules/FindFaceshift.cmake index 1f8b7b19fe..bd77951273 100644 --- a/cmake/modules/FindFaceshift.cmake +++ b/cmake/modules/FindFaceshift.cmake @@ -18,32 +18,9 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("faceshift") - -find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS}) - -if (APPLE) - set(ARCH_DIR "MacOS") -elseif (UNIX) - set(ARCH_DIR "UNIX") -elseif (WIN32) - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(ARCH_DIR "x64") - else() - set(ARCH_DIR "Win32") - endif() -endif () - -find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) -find_library(FACESHIFT_LIBRARY_DEBUG NAME faceshiftd PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) - include(SelectLibraryConfigurations) select_library_configurations(FACESHIFT) -set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY}) - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) - mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) \ No newline at end of file diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index 98b37d5410..9abacac136 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -18,49 +18,6 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("sixense") - -find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS}) - -if (APPLE) - find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) -elseif (UNIX) - find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) - # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) -elseif (WIN32) - if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") - set(ARCH_DIR "x64") - set(ARCH_SUFFIX "_x64") - else() - set(ARCH_DIR "Win32") - set(ARCH_SUFFIX "") - endif() - - find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS}) - find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS}) - - find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS}) - find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS}) - find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS}) -endif () - -include(SelectLibraryConfigurations) -select_library_configurations(SIXENSE) - -set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) -if (WIN32) - list(APPEND SIXENSE_REQUIREMENTS SIXENSE_DEBUG_DLL_PATH SIXENSE_RELEASE_DLL_PATH SIXENSE_DEVICE_DLL_PATH) -endif () - -set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}") - include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS}) - -if (WIN32) - add_paths_to_fixup_libs(${SIXENSE_DEBUG_DLL_PATH} ${SIXENSE_RELEASE_DLL_PATH} ${SIXENSE_DEVICE_DLL_PATH}) -endif () - +find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f9c92c59e7..f04fa88910 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,12 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD") +set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD") + +if(WIN32) + list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient") +endif() + foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) @@ -102,12 +107,14 @@ link_hifi_libraries(shared octree environment gpu gl procedural model render render-utils entities-renderer ui auto-updater plugins display-plugins input-plugins) +#fixme find a way to express faceshift as a plugin target_bullet() target_glew() target_opengl() - -add_dependency_external_projects(sdl2) +if (WIN32 OR APPLE) + target_faceshift() +endif() # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) diff --git a/interface/external/faceshift/readme.txt b/interface/external/faceshift/readme.txt deleted file mode 100644 index 4208711632..0000000000 --- a/interface/external/faceshift/readme.txt +++ /dev/null @@ -1,26 +0,0 @@ - -Instructions for adding the Faceshift library to Interface -Stephen Birarda, July 18th, 2014 - -OS X users: You can also use homebrew to get the Faceshift library by tapping our repo - highfidelity/homebrew-formulas -and then calling 'brew install highfidelity/formulas/faceshift'. - -You can download the Faceshift SDK from http://download.faceshift.com/faceshift-network.zip. - -Create a ‘faceshift’ folder under interface/externals. - -You may optionally choose to place this folder in a location outside the repository (so you can re-use with different checkouts and different projects). - -If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder ‘faceshift’ that contains the lib and include folders. - -1. Build a Faceshift static library from the fsbinarystream.cpp file. - Windows: Win32 console application; no precompiled header or SDL checks; no ATL or MFC headers; Project Properties, Configuration Type = Static Library (.lib). - -2. Copy the library files to the ‘lib’ folder in your Faceshift folder. - OSX: If you build a release version call it libfaceshift.a. The debug version should be called libfaceshiftd.a. - Windows: The release and debug versions should be called faceshift.lib and faceshiftd.lib, respectively. Copy them into a ‘Win32’ folder in your ‘lib’ folder. - -3. Copy the fsbinarystream.h header file from the Faceshift SDK into the ‘include’ folder in your Faceshift folder. - -4. Clear your build directory, run cmake and build, and you should be all set. - diff --git a/interface/external/sdl2/readme.txt b/interface/external/sdl2/readme.txt deleted file mode 100644 index 9f3bd40e15..0000000000 --- a/interface/external/sdl2/readme.txt +++ /dev/null @@ -1,13 +0,0 @@ - -Instructions for adding the SDL library (SDL2) to Interface -David Rowe, 11 Jan 2015 - -You can download the SDL development library from https://www.libsdl.org/. Interface has been tested with version 2.0.3. - -1. Copy the include and lib folders into the interface/externals/sdl2 folder. - This readme.txt should be there as well. - - You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). - If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sdl2' that contains the two folders mentioned above. - -2. Clear your build directory, run cmake and build, and you should be all set. diff --git a/interface/external/sixense/readme.txt b/interface/external/sixense/readme.txt deleted file mode 100644 index a4790caa5e..0000000000 --- a/interface/external/sixense/readme.txt +++ /dev/null @@ -1,10 +0,0 @@ - -Instructions for adding the Sixense driver to Interface -Andrzej Kapolka, November 18, 2013 - -1. Copy the Sixense sdk folders (bin, include, lib, and samples) into the interface/external/Sixense folder. This readme.txt should be there as well. - - You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). - If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sixense' that contains the folders mentioned above. - -3. Delete your build directory, run cmake and build, and you should be all set. diff --git a/libraries/input-plugins/CMakeLists.txt b/libraries/input-plugins/CMakeLists.txt index 094a697012..c26e14e756 100644 --- a/libraries/input-plugins/CMakeLists.txt +++ b/libraries/input-plugins/CMakeLists.txt @@ -11,55 +11,5 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) endif() -#add_dependency_external_projects(Sixense) -#find_package(Sixense REQUIRED) -#target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) -#target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) - -# set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "SDL2" "Sixense") -foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) - string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) - if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) - string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE) - set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_SOURCE_DIR}/interface/external/${${EXTERNAL}_LOWERCASE}") - endif () -endforeach() - -# perform standard include and linking for found externals -foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) - - if (${${EXTERNAL}_UPPERCASE}_REQUIRED) - find_package(${EXTERNAL} REQUIRED) - else () - find_package(${EXTERNAL}) - endif () - - if (${${EXTERNAL}_UPPERCASE}_FOUND AND NOT DISABLE_${${EXTERNAL}_UPPERCASE}) - add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE}) - - # include the library directories (ignoring warnings) - if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS) - set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR}) - endif () - - include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) - - # perform the system include hack for OS X to ignore warnings - if (APPLE) - foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}") - endforeach() - endif () - - if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES) - set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY}) - endif () - - if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE") - target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES}) - elseif (APPLE AND NOT INSTALLER_BUILD) - add_definitions(-DSIXENSE_LIB_FILENAME=\"${${${EXTERNAL}_UPPERCASE}_LIBRARY_RELEASE}\") - endif () - endif () -endforeach() +target_sdl2() +target_sixense() From e12ddf86f6b3fb90c3f51f3e63b322760ac5af84 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 08:33:29 -0700 Subject: [PATCH 0559/1003] add back optional externals --- CMakeLists.txt | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d69b8c4c77..11ef4752f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,19 +186,19 @@ set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) setup_externals_binary_dir() # setup options to grab external project dependencies -#option(GET_BULLET "Get Bullet library automatically as external project" 1) -#option(GET_GLM "Get GLM library automatically as external project" 1) -#option(GET_GVERB "Get Gverb library automatically as external project" 1) -#option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) -#option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) -#option(GET_VHACD "Get V-HACD library automatically as external project" 1) -#option(GET_POLYVOX "Get polyvox library automatically as external project" 1) -#option(GET_OPENVR "Get OpenVR library automatically as external project" 1) -#option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) -#option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) -#option(GET_GLEW "Get GLEW library automatically as external project" 1) -#option(GET_SDL2 "Get SDL2 library automatically as external project" 1) -#option(GET_SIXENSE "Get Sixense library automatically as external project" 1) +option(GET_BULLET "Get Bullet library automatically as external project" 1) +option(GET_GLM "Get GLM library automatically as external project" 1) +option(GET_GVERB "Get Gverb library automatically as external project" 1) +option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) +option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) +option(GET_VHACD "Get V-HACD library automatically as external project" 1) +option(GET_POLYVOX "Get polyvox library automatically as external project" 1) +option(GET_OPENVR "Get OpenVR library automatically as external project" 1) +option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) +option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) +option(GET_GLEW "Get GLEW library automatically as external project" 1) +option(GET_SDL2 "Get SDL2 library automatically as external project" 1) +option(GET_SIXENSE "Get Sixense library automatically as external project" 1) option(USE_NSIGHT "Attempt to find the nSight libraries" 1) From fb3bf163913a2fa7cb79a8c85354c22950cf0274 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 09:01:42 -0700 Subject: [PATCH 0560/1003] improvement to cmake to explicitly use local external dependency --- CMakeLists.txt | 15 --------------- cmake/macros/AddDependencyExternalProjects.cmake | 2 +- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11ef4752f5..efe99b550b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,21 +185,6 @@ set(EXTERNAL_PROJECT_PREFIX "project") set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) setup_externals_binary_dir() -# setup options to grab external project dependencies -option(GET_BULLET "Get Bullet library automatically as external project" 1) -option(GET_GLM "Get GLM library automatically as external project" 1) -option(GET_GVERB "Get Gverb library automatically as external project" 1) -option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) -option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) -option(GET_VHACD "Get V-HACD library automatically as external project" 1) -option(GET_POLYVOX "Get polyvox library automatically as external project" 1) -option(GET_OPENVR "Get OpenVR library automatically as external project" 1) -option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) -option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) -option(GET_GLEW "Get GLEW library automatically as external project" 1) -option(GET_SDL2 "Get SDL2 library automatically as external project" 1) -option(GET_SIXENSE "Get Sixense library automatically as external project" 1) - option(USE_NSIGHT "Attempt to find the nSight libraries" 1) diff --git a/cmake/macros/AddDependencyExternalProjects.cmake b/cmake/macros/AddDependencyExternalProjects.cmake index e8292c3198..e35ca98959 100644 --- a/cmake/macros/AddDependencyExternalProjects.cmake +++ b/cmake/macros/AddDependencyExternalProjects.cmake @@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER) # has the user told us they specific don't want this as an external project? - if (NOT DISABLE_${_PROJ_NAME_UPPER}) + if (NOT USE_LOCAL_${_PROJ_NAME_UPPER}) # have we already detected we can't have this as external project on this OS? if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT) # have we already setup the target? From 7a4787a79cec2d33471ae86ebbb27f511705e2d8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Oct 2015 09:21:13 -0700 Subject: [PATCH 0561/1003] Rename stepSimulation to stepSimulationWithSubstepCallback to fix warning --- libraries/physics/src/PhysicsEngine.cpp | 7 ++++--- libraries/physics/src/ThreadSafeDynamicsWorld.cpp | 5 +++-- libraries/physics/src/ThreadSafeDynamicsWorld.h | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 6773cecb14..2abe4317d7 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -228,7 +228,7 @@ void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { void PhysicsEngine::stepSimulation() { CProfileManager::Reset(); - BT_PROFILE("stepSimulation"); + BT_PROFILE("stepSimulationWithSubstepCallback"); // NOTE: the grand order of operations is: // (1) pull incoming changes // (2) step simulation @@ -241,7 +241,7 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); if (_myAvatarController) { - // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because + // ADEBUG TODO: move this stuff outside and in front of stepSimulationWithSubstepCallback, because // the updateShapeIfNecessary() call needs info from MyAvatar and should // be done on the main thread during the pre-simulation stuff if (_myAvatarController->needsRemoval()) { @@ -263,7 +263,8 @@ void PhysicsEngine::stepSimulation() { updateContactMap(); }; - int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); + int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, + PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); if (numSubsteps > 0) { BT_PROFILE("postSimulation"); _numSubsteps += (uint32_t)numSubsteps; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 8bf7cdab20..94d6315705 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -27,8 +27,9 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { } -int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) { - BT_PROFILE("stepSimulation"); +int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps, + btScalar fixedTimeStep, SubStepCallback onSubStep) { + BT_PROFILE("stepSimulationWithSubstepCallback"); int subSteps = 0; if (maxSubSteps) { //fixed timestep with interpolation diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 1f6ce09d0c..26954832cf 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -37,8 +37,9 @@ public: btConstraintSolver* constraintSolver, btCollisionConfiguration* collisionConfiguration); - // virtual overrides from btDiscreteDynamicsWorld - int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); + int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1, + btScalar fixedTimeStep = btScalar(1.)/btScalar(60.), + SubStepCallback onSubStep = []() { }); void synchronizeMotionStates(); // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated From 84869c512b44ac1ca27b2e6cadd2616ac0ea865c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Oct 2015 09:30:22 -0700 Subject: [PATCH 0562/1003] Fix mis-renamed method names --- libraries/physics/src/PhysicsEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 2abe4317d7..1e652b75c4 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -228,7 +228,7 @@ void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { void PhysicsEngine::stepSimulation() { CProfileManager::Reset(); - BT_PROFILE("stepSimulationWithSubstepCallback"); + BT_PROFILE("stepSimulation"); // NOTE: the grand order of operations is: // (1) pull incoming changes // (2) step simulation @@ -241,7 +241,7 @@ void PhysicsEngine::stepSimulation() { float timeStep = btMin(dt, MAX_TIMESTEP); if (_myAvatarController) { - // ADEBUG TODO: move this stuff outside and in front of stepSimulationWithSubstepCallback, because + // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because // the updateShapeIfNecessary() call needs info from MyAvatar and should // be done on the main thread during the pre-simulation stuff if (_myAvatarController->needsRemoval()) { From 9607812116aac4a02a0efe50a7ac72ec2aa9243d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 30 Oct 2015 10:18:51 -0700 Subject: [PATCH 0563/1003] 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 93503d0a216019cff88a2b08441f88539881932f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 30 Oct 2015 11:03:13 -0700 Subject: [PATCH 0564/1003] make kinematic grab set entities kinematic in bullet and update entity position --- examples/controllers/handControllerGrab.js | 24 ++++++++++++++++------ interface/src/avatar/AvatarActionHold.cpp | 5 +++++ libraries/physics/src/ObjectAction.cpp | 15 +++++++++++++- libraries/physics/src/ObjectMotionState.h | 9 ++++++++ 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index cb445a0960..9ee9ed1e04 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -55,6 +55,7 @@ var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object +var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed ///////////////////////////////////////////////////////////////// // @@ -404,8 +405,9 @@ function MyController(hand, triggerAction) { var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", - "gravity", "ignoreForCollisions"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", "gravity", + "ignoreForCollisions", + "collisionsWillMove"]); var now = Date.now(); // add the action and initialize some variables @@ -549,8 +551,14 @@ function MyController(hand, triggerAction) { this.lineOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, - ["position", "rotation", "gravity", "ignoreForCollisions"]); + ["position", "rotation", "gravity", + "ignoreForCollisions", "collisionsWillMove"]); this.activateEntity(this.grabbedEntity, grabbedProperties); + if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { + Entities.editEntity(this.grabbedEntity, { + collisionsWillMove: false + }); + } var handRotation = this.getHandRotation(); var handPosition = this.getHandPosition(); @@ -579,7 +587,8 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - ttl: ACTION_TTL + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -631,7 +640,8 @@ function MyController(hand, triggerAction) { timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, - ttl: ACTION_TTL + ttl: ACTION_TTL, + kinematic: NEAR_GRABBING_KINEMATIC }); this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); } @@ -828,6 +838,7 @@ function MyController(hand, triggerAction) { if (data["refCount"] == 1) { data["gravity"] = grabbedProperties.gravity; data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; var whileHeldProperties = {gravity: {x:0, y:0, z:0}}; if (invertSolidWhileHeld) { whileHeldProperties["ignoreForCollisions"] = ! grabbedProperties.ignoreForCollisions; @@ -845,7 +856,8 @@ function MyController(hand, triggerAction) { if (data["refCount"] < 1) { Entities.editEntity(entityID, { gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"] + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] }); data = null; } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 238e48d2fd..59bfd88be6 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -119,6 +119,9 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { worldTrans.setRotation(glmToBullet(_rotationalTarget)); rigidBody->setWorldTransform(worldTrans); + ownerEntity->setPosition(_positionalTarget); + ownerEntity->setRotation(_rotationalTarget); + _previousPositionalTarget = _positionalTarget; _previousRotationalTarget = _rotationalTarget; _previousSet = true; @@ -224,6 +227,8 @@ QVariantMap AvatarActionHold::getArguments() { arguments["relativeRotation"] = glmToQMap(_relativeRotation); arguments["timeScale"] = _linearTimeScale; arguments["hand"] = _hand; + arguments["kinematic"] = _kinematic; + arguments["kinematicSetVelocity"] = _kinematicSetVelocity; }); return arguments; } diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index ff8382a143..d44ebc30b1 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -35,7 +35,9 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta if (ownerEntityExpired) { qDebug() << "warning -- action with no entity removing self from btCollisionWorld."; btDynamicsWorld* dynamicsWorld = static_cast(collisionWorld); - dynamicsWorld->removeAction(this); + if (dynamicsWorld) { + dynamicsWorld->removeAction(this); + } return; } @@ -120,6 +122,17 @@ QVariantMap ObjectAction::getArguments() { arguments["ttl"] = (float)(_expires - now) / (float)USECS_PER_SECOND; } arguments["tag"] = _tag; + + EntityItemPointer entity = _ownerEntity.lock(); + if (entity) { + ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState) { + arguments["::active"] = motionState->isActive(); + arguments["::motion-type"] = motionTypeToString(motionState->getMotionType()); + } else { + arguments["::no-motion-state"] = true; + } + } }); return arguments; } diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 450ac34a90..22f0e9dcb0 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -29,6 +29,15 @@ enum MotionType { MOTION_TYPE_KINEMATIC // keyframed motion }; +inline QString motionTypeToString(MotionType motionType) { + switch(motionType) { + case MOTION_TYPE_STATIC: return QString("static"); + case MOTION_TYPE_DYNAMIC: return QString("dynamic"); + case MOTION_TYPE_KINEMATIC: return QString("kinematic"); + } + return QString("unknown"); +} + enum MotionStateType { MOTIONSTATE_TYPE_INVALID, MOTIONSTATE_TYPE_ENTITY, From f887bbd45c1a954a3f34cb29d430407541c507c3 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 30 Oct 2015 11:06:20 -0700 Subject: [PATCH 0565/1003] 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 ce1d0fe07c1d89e3bff075a2e31455d5e58688a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 30 Oct 2015 11:25:54 -0700 Subject: [PATCH 0566/1003] Git rid of unused (un-emitted and intended to be removed long ago) script signal. --- libraries/entities/src/EntityScriptingInterface.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d764cd7bab..156f16cf46 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -152,7 +152,6 @@ public slots: Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); signals: - void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void canAdjustLocksChanged(bool canAdjustLocks); From 393f6a4c763df19b220fe077179c2d1f66f5e956 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 30 Oct 2015 11:35:50 -0700 Subject: [PATCH 0567/1003] 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 b102081f2f1dd0a0bc63c9f7d9c9972c7cb2eb38 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 30 Oct 2015 11:35:51 -0700 Subject: [PATCH 0568/1003] upon release, set velocity after returning entity to being dynamic --- examples/controllers/handControllerGrab.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9ee9ed1e04..353e7b3daa 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -588,7 +588,8 @@ function MyController(hand, triggerAction) { relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -641,7 +642,8 @@ function MyController(hand, triggerAction) { relativePosition: this.offsetPosition, relativeRotation: this.offsetRotation, ttl: ACTION_TTL, - kinematic: NEAR_GRABBING_KINEMATIC + kinematic: NEAR_GRABBING_KINEMATIC, + kinematicSetVelocity: true }); this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); } @@ -810,12 +812,13 @@ function MyController(hand, triggerAction) { Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); } + this.deactivateEntity(this.grabbedEntity); + // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. Entities.editEntity(this.grabbedEntity, { velocity: this.grabbedVelocity }); - this.deactivateEntity(this.grabbedEntity); this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; From ce7fed2292cc4b81f975c4022cf7d57a3755367c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 13:16:31 -0700 Subject: [PATCH 0569/1003] 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 f652e983a979daa9d4007f3e0ff2836563fa9779 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 30 Oct 2015 14:53:29 -0700 Subject: [PATCH 0570/1003] flag kinematic objs when changed in stepSimulation() --- interface/src/avatar/AvatarActionHold.cpp | 3 +- libraries/physics/src/ObjectAction.cpp | 1 - libraries/physics/src/ObjectAction.h | 10 ++--- libraries/physics/src/ObjectMotionState.cpp | 10 ++--- libraries/physics/src/ObjectMotionState.h | 16 ++++--- .../physics/src/ThreadSafeDynamicsWorld.cpp | 43 +++++++++++++++---- .../physics/src/ThreadSafeDynamicsWorld.h | 9 ++-- 7 files changed, 62 insertions(+), 30 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 59bfd88be6..e7d030e17a 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -119,8 +119,7 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { worldTrans.setRotation(glmToBullet(_rotationalTarget)); rigidBody->setWorldTransform(worldTrans); - ownerEntity->setPosition(_positionalTarget); - ownerEntity->setRotation(_rotationalTarget); + motionState->dirtyInternalKinematicChanges(); _previousPositionalTarget = _positionalTarget; _previousRotationalTarget = _rotationalTarget; diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index d44ebc30b1..3188283f68 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -16,7 +16,6 @@ ObjectAction::ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) : btActionInterface(), EntityActionInterface(type, id), - _active(false), _ownerEntity(ownerEntity) { } diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index fca446aec4..45b40a9fb3 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -50,6 +50,8 @@ public: virtual quint64 getExpires() { return _expires; } protected: + quint64 localTimeToServerTime(quint64 timeValue) const; + quint64 serverTimeToLocalTime(quint64 timeValue) const; virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); @@ -62,14 +64,10 @@ protected: virtual void setAngularVelocity(glm::vec3 angularVelocity); virtual void activateBody(); - bool _active; EntityItemWeakPointer _ownerEntity; - - quint64 _expires; // in seconds since epoch QString _tag; - - quint64 localTimeToServerTime(quint64 timeValue) const; - quint64 serverTimeToLocalTime(quint64 timeValue) const; + quint64 _expires { 0 }; // in seconds since epoch + bool _active { false }; private: int getEntityServerClockSkew() const; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 4f3d0396c6..be0edafff5 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -20,17 +20,17 @@ // origin of physics simulation in world-frame glm::vec3 _worldOffset(0.0f); -// static +// static void ObjectMotionState::setWorldOffset(const glm::vec3& offset) { _worldOffset = offset; } -// static +// static const glm::vec3& ObjectMotionState::getWorldOffset() { return _worldOffset; } -// static +// static uint32_t worldSimulationStep = 0; void ObjectMotionState::setWorldSimulationStep(uint32_t step) { assert(step > worldSimulationStep); @@ -41,7 +41,7 @@ uint32_t ObjectMotionState::getWorldSimulationStep() { return worldSimulationStep; } -// static +// static ShapeManager* shapeManager = nullptr; void ObjectMotionState::setShapeManager(ShapeManager* manager) { assert(manager); @@ -85,7 +85,7 @@ glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { glm::vec3 ObjectMotionState::getBodyLinearVelocityGTSigma() const { // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates - // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving + // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving // just under this velocity threshold would trigger an update about V/dX times per second. const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 22f0e9dcb0..992bdd11d7 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -44,12 +44,12 @@ enum MotionStateType { MOTIONSTATE_TYPE_AVATAR }; -// The update flags trigger two varieties of updates: "hard" which require the body to be pulled +// The update flags trigger two varieties of updates: "hard" which require the body to be pulled // and re-added to the physics engine and "easy" which just updates the body properties. -const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | +const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_MOTION_TYPE | Simulation::DIRTY_SHAPE | Simulation::DIRTY_COLLISION_GROUP); const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES | - Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | + Simulation::DIRTY_MASS | Simulation::DIRTY_MATERIAL | Simulation::DIRTY_SIMULATOR_ID | Simulation::DIRTY_SIMULATOR_OWNERSHIP); // These are the set of incoming flags that the PhysicsEngine needs to hear about: @@ -66,7 +66,7 @@ class PhysicsEngine; class ObjectMotionState : public btMotionState { public: // These poroperties of the PhysicsEngine are "global" within the context of all ObjectMotionStates - // (assuming just one PhysicsEngine). They are cached as statics for fast calculations in the + // (assuming just one PhysicsEngine). They are cached as statics for fast calculations in the // ObjectMotionState context. static void setWorldOffset(const glm::vec3& offset); static const glm::vec3& getWorldOffset(); @@ -121,7 +121,7 @@ public: virtual float getObjectFriction() const = 0; virtual float getObjectLinearDamping() const = 0; virtual float getObjectAngularDamping() const = 0; - + virtual glm::vec3 getObjectPosition() const = 0; virtual glm::quat getObjectRotation() const = 0; virtual glm::vec3 getObjectLinearVelocity() const = 0; @@ -140,6 +140,11 @@ public: bool isActive() const { return _body ? _body->isActive() : false; } + bool hasInternalKinematicChanges() const { return _hasInternalKinematicChanges; } + + void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; } + void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; } + friend class PhysicsEngine; protected: @@ -160,6 +165,7 @@ protected: float _mass; uint32_t _lastKinematicStep; + bool _hasInternalKinematicChanges { false }; }; typedef QSet SetOfMotionStates; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index 94d6315705..c7dddb95c4 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -4,8 +4,8 @@ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it freely, + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it freely, * subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -75,7 +75,7 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep } } - // NOTE: We do NOT call synchronizeMotionState() after each substep (to avoid multiple locks on the + // NOTE: We do NOT call synchronizeMotionStates() after each substep (to avoid multiple locks on the // object data outside of the physics engine). A consequence of this is that the transforms of the // external objects only ever update at the end of the full step. @@ -87,6 +87,33 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep return subSteps; } +// call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() +void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { + btAssert(body); + if (body->getMotionState() && !body->isStaticObject()) { + //we need to call the update at least once, even for sleeping objects + //otherwise the 'graphics' transform never updates properly + ///@todo: add 'dirty' flag + //if (body->getActivationState() != ISLAND_SLEEPING) + { + if (body->isKinematicObject()) { + ObjectMotionState* objectMotionState = static_cast(body->getMotionState()); + if (!objectMotionState->hasInternalKinematicChanges()) { + return; + } else { + objectMotionState->clearInternalKinematicChanges(); + } + } + btTransform interpolatedTransform; + btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), + body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(), + (m_latencyMotionStateInterpolation && m_fixedTimeStep) ? m_localTime - m_fixedTimeStep : m_localTime*body->getHitFraction(), + interpolatedTransform); + body->getMotionState()->setWorldTransform(interpolatedTransform); + } + } +} + void ThreadSafeDynamicsWorld::synchronizeMotionStates() { _changedMotionStates.clear(); BT_PROFILE("synchronizeMotionStates"); @@ -97,22 +124,22 @@ void ThreadSafeDynamicsWorld::synchronizeMotionStates() { btRigidBody* body = btRigidBody::upcast(colObj); if (body) { if (body->getMotionState()) { - synchronizeSingleMotionState(body); + synchronizeMotionState(body); _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } - } else { + } else { //iterate over all active rigid bodies for (int i=0;iisActive()) { if (body->getMotionState()) { - synchronizeSingleMotionState(body); + synchronizeMotionState(body); _changedMotionStates.push_back(static_cast(body->getMotionState())); } } } - } -} + } +} diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 26954832cf..de37554f56 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -4,8 +4,8 @@ * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it freely, + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it freely, * subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -43,13 +43,16 @@ public: void synchronizeMotionStates(); // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated - // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide + // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide // smoother rendering of objects when the physics simulation loop is ansynchronous to the render loop). float getLocalTimeAccumulation() const { return m_localTime; } VectorOfMotionStates& getChangedMotionStates() { return _changedMotionStates; } private: + // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() + void synchronizeMotionState(btRigidBody* body); + VectorOfMotionStates _changedMotionStates; }; From 22324eb281f7b3f46f91fdbe06c04f0196976f13 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 30 Oct 2015 14:55:33 -0700 Subject: [PATCH 0571/1003] 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 0572/1003] 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 cf83ca22bbaa77a195268e49ff720096f61d5a15 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 30 Oct 2015 15:30:54 -0700 Subject: [PATCH 0573/1003] change how kinematic objects get data back to entity when an action is active --- examples/controllers/handControllerGrab.js | 64 ++++++++++++++----- libraries/physics/src/EntityMotionState.cpp | 3 +- .../physics/src/ThreadSafeDynamicsWorld.cpp | 2 + 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 353e7b3daa..10775a483f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -80,6 +80,18 @@ var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; + +var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js +var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js + +var DEFAULT_GRABBABLE_DATA = { + grabbable: true, + invertSolidWhileHeld: false +}; + +var disabledHand ='none'; + + // states for the state machine var STATE_OFF = 0; var STATE_SEARCHING = 1; @@ -93,15 +105,35 @@ var STATE_FAR_GRABBING_NON_COLLIDING = 8; var STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING = 9; var STATE_RELEASE = 10; -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js -var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js -var DEFAULT_GRABBABLE_DATA = { - grabbable: true, - invertSolidWhileHeld: false -}; +function stateToName(state) { + switch (state) { + case STATE_OFF: + return "off"; + case STATE_SEARCHING: + return "searching"; + case STATE_DISTANCE_HOLDING: + return "distance_holding"; + case STATE_CONTINUE_DISTANCE_HOLDING: + return "continue_distance_holding"; + case STATE_NEAR_GRABBING: + return "near_grabbing"; + case STATE_CONTINUE_NEAR_GRABBING: + return "continue_near_grabbing"; + case STATE_NEAR_GRABBING_NON_COLLIDING: + return "near_grabbing_non_colliding"; + case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING: + return "continue_near_grabbing_non_colliding"; + case STATE_FAR_GRABBING_NON_COLLIDING: + return "far_grabbing_non_colliding"; + case STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING: + return "continue_far_grabbing_non_colliding"; + case STATE_RELEASE: + return "release"; + } -var disabledHand ='none'; + return "unknown"; +} function getTag() { return "grab-" + MyAvatar.sessionUUID; @@ -196,7 +228,7 @@ function MyController(hand, triggerAction) { this.setState = function(newState) { if (WANT_DEBUG) { - print("STATE: " + this.state + " --> " + newState); + print("STATE: " + stateToName(this.state) + " --> " + newState + ", hand: " + this.hand); } this.state = newState; } @@ -348,10 +380,11 @@ function MyController(hand, triggerAction) { } if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - if (intersection.properties.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else { + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } else { + this.setState(STATE_NEAR_GRABBING); } } else { // don't allow two people to distance grab the same object @@ -392,11 +425,11 @@ function MyController(hand, triggerAction) { } if (this.grabbedEntity === null) { return; - } else if (props.locked === 0 && props.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else if (props.collisionsWillMove === 0 && grabbableData.wantsTrigger) { - // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event + } + if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } else if (props.locked === 0) { + this.setState(STATE_NEAR_GRABBING); } } }; @@ -816,6 +849,7 @@ function MyController(hand, triggerAction) { // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. + print("release velocity is " + vec3toStr(this.grabbedVelocity)); Entities.editEntity(this.grabbedEntity, { velocity: this.grabbedVelocity }); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 6832b8daff..42aaea33c2 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -146,7 +146,7 @@ MotionType EntityMotionState::computeObjectMotionType() const { if (_entity->getCollisionsWillMove()) { return MOTION_TYPE_DYNAMIC; } - return _entity->isMoving() ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; + return (_entity->isMoving() || _entity->hasActions()) ? MOTION_TYPE_KINEMATIC : MOTION_TYPE_STATIC; } bool EntityMotionState::isMoving() const { @@ -184,6 +184,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { if (!_entity) { return; } + assert(entityTreeIsLocked()); measureBodyAcceleration(); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index c7dddb95c4..f725315330 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -102,6 +102,8 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { return; } else { objectMotionState->clearInternalKinematicChanges(); + body->getMotionState()->setWorldTransform(body->getWorldTransform()); + return; } } btTransform interpolatedTransform; From 085282db4f4cf1ed69ec4489c3906a3ab9f32bcf Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 30 Oct 2015 16:13:04 -0700 Subject: [PATCH 0574/1003] 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 0575/1003] 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 944f0965c059a1d9af6a30d8c18523d59c836cff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 30 Oct 2015 16:35:50 -0700 Subject: [PATCH 0576/1003] some refactoring and a bug fix -- grab script can throw things again --- examples/controllers/handControllerGrab.js | 8 +------- libraries/physics/src/PhysicsEngine.cpp | 9 ++++++--- libraries/physics/src/ThreadSafeDynamicsWorld.cpp | 6 ++---- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 10775a483f..815f903bfa 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -15,13 +15,11 @@ Script.include("../libraries/utils.js"); -//////////////////////////////////////////////////////////// // // add lines where the hand ray picking is happening // var WANT_DEBUG = false; -///////////////////////////////////////////////////////////////// // // these tune time-averaging and "on" value for analog trigger // @@ -30,7 +28,6 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value var TRIGGER_ON_VALUE = 0.4; var TRIGGER_OFF_VALUE = 0.15; -///////////////////////////////////////////////////////////////// // // distant manipulation // @@ -44,7 +41,6 @@ var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000,z: 1000}; var LINE_LENGTH = 500; var PICK_MAX_DISTANCE = 500; // max length of pick-ray -///////////////////////////////////////////////////////////////// // // near grabbing // @@ -57,7 +53,6 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed -///////////////////////////////////////////////////////////////// // // other constants // @@ -228,7 +223,7 @@ function MyController(hand, triggerAction) { this.setState = function(newState) { if (WANT_DEBUG) { - print("STATE: " + stateToName(this.state) + " --> " + newState + ", hand: " + this.hand); + print("STATE: " + stateToName(this.state) + " --> " + stateToName(newState) + ", hand: " + this.hand); } this.state = newState; } @@ -849,7 +844,6 @@ function MyController(hand, triggerAction) { // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. - print("release velocity is " + vec3toStr(this.grabbedVelocity)); Entities.editEntity(this.grabbedEntity, { velocity: this.grabbedVelocity }); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 1e652b75c4..f3ef855e50 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -82,12 +82,12 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { btCollisionShape* shape = motionState->getShape(); assert(shape); body = new btRigidBody(mass, motionState, shape, inertia); + motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); } body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); body->updateInertiaTensor(); - motionState->setRigidBody(body); motionState->updateBodyVelocities(); const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec @@ -101,12 +101,15 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { shape->calculateLocalInertia(mass, inertia); if (!body) { body = new btRigidBody(mass, motionState, shape, inertia); + motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); } + body->setCollisionFlags(body->getCollisionFlags() & ~(btCollisionObject::CF_KINEMATIC_OBJECT | + btCollisionObject::CF_STATIC_OBJECT)); body->updateInertiaTensor(); - motionState->setRigidBody(body); motionState->updateBodyVelocities(); + // NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds. // (the 2 seconds is determined by: static btRigidBody::gDeactivationTime const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec @@ -123,12 +126,12 @@ void PhysicsEngine::addObject(ObjectMotionState* motionState) { if (!body) { assert(motionState->getShape()); body = new btRigidBody(mass, motionState, motionState->getShape(), inertia); + motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); } body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); - motionState->setRigidBody(body); break; } } diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index f725315330..d06a9b8e07 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -98,13 +98,11 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) { { if (body->isKinematicObject()) { ObjectMotionState* objectMotionState = static_cast(body->getMotionState()); - if (!objectMotionState->hasInternalKinematicChanges()) { - return; - } else { + if (objectMotionState->hasInternalKinematicChanges()) { objectMotionState->clearInternalKinematicChanges(); body->getMotionState()->setWorldTransform(body->getWorldTransform()); - return; } + return; } btTransform interpolatedTransform; btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), From f50e1a0977f92d28c5e117f80a2e7723e6acd8ae Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 30 Oct 2015 17:18:37 -0700 Subject: [PATCH 0577/1003] 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 00be6b3e3a12ee2de08360b484fd73fe3a2d8879 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 30 Oct 2015 17:32:26 -0700 Subject: [PATCH 0578/1003] Add support for NOT conditional in the route json --- .../src/controllers/UserInputMapper.cpp | 19 ++++++++++++++++++- .../impl/conditionals/EndpointConditional.h | 8 ++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index d33e215797..6462103290 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -825,12 +825,29 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) return std::make_shared(children); } else if (value.isString()) { // Support "when" : "GamePad.RB" - auto input = findDeviceInput(value.toString()); + auto conditionalToken = value.toString(); + + // Detect for modifier case (Not...) + QString conditionalModifier; + const QString JSON_CONDITIONAL_MODIFIER_NOT("!"); + if (conditionalToken.startsWith(JSON_CONDITIONAL_MODIFIER_NOT)) { + conditionalModifier = JSON_CONDITIONAL_MODIFIER_NOT; + conditionalToken = conditionalToken.right(conditionalToken.size() - conditionalModifier.size()); + } + + auto input = findDeviceInput(conditionalToken); auto endpoint = endpointFor(input); if (!endpoint) { return Conditional::Pointer(); } + if (!conditionalModifier.isEmpty()) { + if (conditionalModifier == JSON_CONDITIONAL_MODIFIER_NOT) { + return std::make_shared(endpoint); + } + } + + // Default and conditional behavior return std::make_shared(endpoint); } diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 1e4205afc7..8f0499077e 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -23,5 +23,13 @@ private: Endpoint::Pointer _endpoint; }; +class NotEndpointConditional : public Conditional { +public: + NotEndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} + virtual bool satisfied() override { return _endpoint && _endpoint->value() == 0.0; } +private: + Endpoint::Pointer _endpoint; +}; + } #endif From 2eb62f2fd89fb9c49107a72bdab9106dc00a8efa Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 30 Oct 2015 17:44:06 -0700 Subject: [PATCH 0579/1003] 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; From 46d87dda5a8d4f77e74718da6e43cd635a153903 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 30 Oct 2015 19:00:01 -0700 Subject: [PATCH 0580/1003] Fixing paths for SDL2 and Sixense libs --- cmake/externals/sdl2/CMakeLists.txt | 42 ++++++++++++++++++-------- cmake/externals/sixense/CMakeLists.txt | 12 ++++++++ libraries/entities/CMakeLists.txt | 2 +- libraries/procedural/CMakeLists.txt | 2 +- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index 0f44f28610..abd436d571 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -2,6 +2,8 @@ set(EXTERNAL_NAME sdl2) include(ExternalProject) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} @@ -13,15 +15,33 @@ if (WIN32) LOG_DOWNLOAD 1 ) elseif (APPLE) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3-OSX.tar.gz - URL_MD5 64f888886268bdf1656ef1b4b7d7756d - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) + + ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DVIDEO_OPENGL=OFF + BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 + ) + + ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") + set(${EXTERNAL_NAME_UPPER}_LIBRARY "${INSTALL_DIR}/lib/libSDL2-2.0.dylib" CACHE STRING "Path to SDL2 library") + + set(_SDL2_LIB_DIR "${INSTALL_DIR}/lib") + + ExternalProject_Add_Step( + ${EXTERNAL_NAME} + change-install-name + COMMENT "Calling install_name_tool on SDL2 libraries to fix install name for dylib linking" + COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SDL2_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake + DEPENDEES install + WORKING_DIRECTORY + LOG 1 + ) + else () if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") @@ -41,12 +61,8 @@ endif () # Hide this external target (for ide users) set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") -string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (APPLE) - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library") elseif (WIN32) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index 72de4a7e15..dea15485dc 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -37,6 +37,18 @@ elseif(APPLE) set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) + set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64/release_dll") + + ExternalProject_Add_Step( + ${EXTERNAL_NAME} + change-install-name-release + COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" + COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake + DEPENDEES install + WORKING_DIRECTORY + LOG 1 + ) + elseif(NOT ANDROID) # FIXME need to account for different architectures diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index f6b2e0e280..26e90f5d41 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) -link_hifi_libraries(avatars shared octree gpu model fbx networking animation environment) +link_hifi_libraries(avatars shared audio octree gpu model fbx networking animation environment) target_bullet() diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index 0483b8d3a8..37c4fbfbe8 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME procedural) AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() -link_hifi_libraries(shared gpu model model-networking) +link_hifi_libraries(shared gpu networking model model-networking) From 0a083c070f9c00b719431da0f1d07e429b7b6587 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 30 Oct 2015 20:21:53 -0700 Subject: [PATCH 0581/1003] color busters cooperative game --- examples/color_busters/colorBusterWand.js | 226 ++++++++++++++++++ .../color_busters/createColorBusterCubes.js | 89 +++++++ .../color_busters/createColorBusterWand.js | 55 +++++ 3 files changed, 370 insertions(+) create mode 100644 examples/color_busters/colorBusterWand.js create mode 100644 examples/color_busters/createColorBusterCubes.js create mode 100644 examples/color_busters/createColorBusterWand.js diff --git a/examples/color_busters/colorBusterWand.js b/examples/color_busters/colorBusterWand.js new file mode 100644 index 0000000000..26948da91b --- /dev/null +++ b/examples/color_busters/colorBusterWand.js @@ -0,0 +1,226 @@ +(function() { + + //seconds that the combination lasts + var COMBINED_COLOR_DURATION = 10; + + var INDICATOR_OFFSET_UP = 0.5; + + var REMOVE_CUBE_SOUND_URL = ''; + var COMBINE_COLORS_SOUND_URL = ''; + + var _this; + + function ColorWand() { + _this = this; + }; + + ColorBusterWand.prototype = { + combinedColorsTimer: null, + + preload: function(entityID) { + print("preload"); + this.entityID = entityID; + this.REMOVE_CUBE_SOUND = SoundCache.getSound(REMOVE_CUBE_SOUND_URL); + this.COMBINE_COLORS_SOUND = SoundCache.getSound(COMBINE_COLORS_SOUND_URL); + + }, + + entityCollisionWithEntity: function(me, otherEntity, collision) { + + var otherProperties = Entities.getEntityProperties(otherEntity, ["name", "userData"]); + var myProperties = Entities.getEntityProperties(otherEntity, ["userData"]); + var myUserData = JSON.parse(myProperties.userData); + var otherUserData = JSON.parse(otherProperties.userData); + + if (otherProperties.name === 'Hifi-ColorBusterWand') { + if (otherUserData.hifiColorBusterWandKey.colorLocked !== true && myUserData.hifiColorBusterWandKey.colorLocked !== true) { + if (otherUserData.hifiColorBusterWandKey.originalColorName === myUserData.hifiColorBusterWandKey.originalColorName) { + return; + } else { + this.combineColorsWithOtherWand(otherUserData.hifiColorBusterWandKey.originalColorName, myUserData.hifiColorBusterWandKey.originalColorName); + } + } + } + + if (otherProperties.name === 'Hifi-ColorBusterCube') { + if (otherUserData.hifiColorBusterCubeKey.originalColorName === myUserData.hifiColorBusterWandKey.currentColor) { + removeCubeOfSameColor(otherEntity); + } + + } + }, + + combineColorsWithOtherWand: function(otherColor, myColor) { + var newColor; + + if ((otherColor === 'red' && myColor == 'yellow') || (myColor === 'red' && otherColor === 'yellow')) { + //orange + newColor = 'orange'; + } + + if ((otherColor === 'red' && myColor == 'blue') || (myColor === 'red' && otherColor === 'blue')) { + //violet + newColor = 'violet'; + } + + if ((otherColor === 'blue' && myColor == 'yellow') || (myColor === 'blue' && otherColor === 'yellow')) { + //green. + newColor = 'green'; + } + + _this.combinedColorsTimer = Script.setTimeout(function() { + _this.resetToOriginalColor(myColor); + _this.combinedColorsTimer = null; + }, COMBINED_COLOR_DURATION * 1000); + + setEntityCustomData(hifiColorWandKey, this.entityID, { + owner: MyAvatar.sessionUUID, + currentColor: newColor + originalColorName: myColor, + colorLocked: false + }); + + _this.setCurrentColor(newColor); + + }, + + setCurrentColor: function(newColor) { + + var color; + + if (newColor === 'orange') { + color = { + red: 255, + green: 165, + blue: 0 + }; + } + + if (newColor === 'violet') { + color = { + red: 128, + green: 0, + blue: 128 + }; + } + + if (newColor === 'green') { + color = { + red: 0, + green: 255, + blue: 0 + }; + } + + if (newColor === 'red') { + color = { + red: 255, + green: 0, + blue: 0 + }; + } + + if (newColor === 'yellow') { + color = { + red: 255, + green: 255, + blue: 0 + }; + } + + if (newColor === 'blue') { + color = { + red: 0, + green: 0, + blue: 255 + }; + } + + Entities.editEntity(this.colorIndicator, { + color: color + }) + }, + + resetToOriginalColor: function(myColor) { + setEntityCustomData(hifiColorWandKey, this.entityID, { + owner: MyAvatar.sessionUUID, + currentColor: myColor + originalColorName: myColor, + colorLocked: true + }); + + this.setCurrentColor(myColor); + }, + + removeCubeOfSameColor: function(cube) { + Entities.callEntityMethod(cube, 'cubeEnding'); + Entities.deleteEntity(cube); + }, + + startNearGrab: function() { + this.currentProperties = Entities.getEntityProperties(this.entityID); + this.createColorIndicator(); + }, + + continueNearGrab: function() { + this.currentProperties = Entities.getEntityProperties(this.entityID); + this.updateColorIndicatorLocation(); + }, + + releaseGrab: function() { + this.deleteEntity(this.colorIndicator); + if (this.combinedColorsTimer !== null) { + Script.clearTimeout(this.combinedColorsTimer); + } + + }, + + createColorIndicator: function() { + var properties = { + name: 'Hifi-ColorBusterIndicator', + type: 'Box', + dimensions: COLOR_INDICATOR_DIMENSIONS, + color: this.currentProperties.position, + position: this.currentProperties.position + } + + this.colorIndicator = Entities.addEntity(properties); + }, + + updateColorIndicatorLocation: function() { + + var position; + + var upVector = Quat.getUp(this.currentProperties.rotation); + var indicatorVector = Vec3.multiply(upVector, INDICATOR_OFFSET_UP); + position = Vec3.sum(this.currentProperties.position, indicatorVector); + + var properties = { + position: position, + rotation: this.currentProperties.rotation + } + + Entities.editEntity(this.colorIndicator, properties); + }, + + + playSoundAtCurrentPosition: function(removeCubeSound) { + var position = Entities.getEntityProperties(this.entityID, "position").position; + + var audioProperties = { + volume: 0.25, + position: position + }; + + if (removeCubeSound) { + Audio.playSound(this.REMOVE_CUBE_SOUND, audioProperties); + } else { + Audio.playSound(this.COMBINE_COLORS_SOUND, audioProperties); + } + }, + + + }; + + return new ColorBusterWand(); +}); \ No newline at end of file diff --git a/examples/color_busters/createColorBusterCubes.js b/examples/color_busters/createColorBusterCubes.js new file mode 100644 index 0000000000..ad7b8b5307 --- /dev/null +++ b/examples/color_busters/createColorBusterCubes.js @@ -0,0 +1,89 @@ +var CUBE_DIMENSIONS = { + x: 1, + y: 1, + z: 1 +}; + +var NUMBER_OF_CUBES_PER_SIDE: 20; + +var STARTING_CORNER_POSITION = { + x: 0, + y: 0, + z: 0 +} + +var STARTING_COLORS = [ + ['red', { + red: 255, + green: 0, + blue: 0 + }], + ['yellow', { + red: 255, + green: 255, + blue: 0 + }], + ['blue', { + red: 0, + green: 0, + blue: 255 + }], + ['orange', { + red: 255, + green: 165, + blue: 0 + }], + ['violet', { + red: 128, + green: 0, + blue: 128 + }], + ['green', { + red: 0, + green: 255, + blue: 0 + }] +] + +function chooseStartingColor() { + var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)]; + return startingColor +} + +function createColorBusterCube(row, column, vertical) { + + var startingColor = chooseStartingColor(); + var colorBusterCubeProperties = { + name: 'Hifi-ColorBusterWand', + type: 'Model', + url: COLOR_WAND_MODEL_URL, + dimensions: COLOR_WAND_DIMENSIONS, + position: { + x: row, + y: vertical, + z: column + } + userData: JSON.stringify({ + hifiColorBusterCubeKey: { + originalColorName: startingColor[0], + + } + }) + }; + + return Entities.addEntity(colorBusterCubeProperties); +} + +function createBoard() { + var vertical; + for (vertical = 0; vertical === NUMBER_OF_CUBES_PER_SIDE) { + var row; + var column; + //create a single layer + for (row = 0; row === NUMBER_OF_CUBES_PER_SIDE; row++) { + for (column = 0; column === NUMBER_OF_CUBES_PER_SIDE; column++) { + this.createColorBusterCube(row, column, vertical) + } + } + } +} \ No newline at end of file diff --git a/examples/color_busters/createColorBusterWand.js b/examples/color_busters/createColorBusterWand.js new file mode 100644 index 0000000000..68e6ecfee5 --- /dev/null +++ b/examples/color_busters/createColorBusterWand.js @@ -0,0 +1,55 @@ +var COLOR_WAND_MODEL_URL = ''; +var COLOR_WAND_DIMENSIONS = { + x: 0, + y: 0, + z: 0 +}; +var COLOR_WAND_START_POSITION = { + x: 0, + y: 0, + z: 0 +}; +var STARTING_COLORS = [ + ['red', { + red: 255, + green: 0, + blue: 0 + }], + ['yellow', { + red: 255, + green: 255, + blue: 0 + }], + ['blue', { + red: 0, + green: 0, + blue: 255 + }] +] + +function chooseStartingColor() { + var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)]; + return startingColor +} + +function createColorBusterWand() { + + var startingColor = chooseStartingColor(); + var colorBusterWandProperties = { + name: 'Hifi-ColorBusterWand', + type: 'Model', + url: COLOR_WAND_MODEL_URL, + dimensions: COLOR_WAND_DIMENSIONS, + position: COLOR_WAND_START_POSITION, + userData: JSON.stringify({ + hifiColorBusterWandKey: { + owner: MyAvatar.sessionUUID, + currentColor: startingColor[1] + originalColorName: startingColor[0], + colorLocked: false + } + }) + }; + + Entities.addEntity(colorBusterWandProperties); +} \ No newline at end of file From 459b449a7fed46786541bebcd5691a8a3b8bbfbf Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 30 Oct 2015 20:52:19 -0700 Subject: [PATCH 0582/1003] rename folder --- examples/{color_busters => color_busters_game}/colorBusterWand.js | 0 .../createColorBusterCubes.js | 0 .../createColorBusterWand.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename examples/{color_busters => color_busters_game}/colorBusterWand.js (100%) rename examples/{color_busters => color_busters_game}/createColorBusterCubes.js (100%) rename examples/{color_busters => color_busters_game}/createColorBusterWand.js (100%) diff --git a/examples/color_busters/colorBusterWand.js b/examples/color_busters_game/colorBusterWand.js similarity index 100% rename from examples/color_busters/colorBusterWand.js rename to examples/color_busters_game/colorBusterWand.js diff --git a/examples/color_busters/createColorBusterCubes.js b/examples/color_busters_game/createColorBusterCubes.js similarity index 100% rename from examples/color_busters/createColorBusterCubes.js rename to examples/color_busters_game/createColorBusterCubes.js diff --git a/examples/color_busters/createColorBusterWand.js b/examples/color_busters_game/createColorBusterWand.js similarity index 100% rename from examples/color_busters/createColorBusterWand.js rename to examples/color_busters_game/createColorBusterWand.js From 9b9e35d3963e556842c243da6bd8a33f2f9a4a26 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 30 Oct 2015 22:01:43 -0700 Subject: [PATCH 0583/1003] Limit sixense to mavericks and below --- cmake/externals/sixense/CMakeLists.txt | 21 ++++++++++++++----- cmake/modules/FindSixense.cmake | 4 ++++ .../src/input-plugins/SixenseManager.cpp | 8 +++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index dea15485dc..5edccb2c88 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -35,20 +35,31 @@ if (WIN32) elseif(APPLE) - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) - - set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64/release_dll") - + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${SOURCE_DIR}/lib/osx_x64/debug_dll/libsixensed_x64.dylib CACHE TYPE INTERNAL) + + set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64") ExternalProject_Add_Step( ${EXTERNAL_NAME} change-install-name-release COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" - COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake + COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/release_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake DEPENDEES install WORKING_DIRECTORY LOG 1 ) + set(_SIXENSE_LIB_DIR "${SOURCE_DIR}/lib/osx_x64") + ExternalProject_Add_Step( + ${EXTERNAL_NAME} + change-install-name-debug + COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking" + COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_SIXENSE_LIB_DIR}/debug_dll -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake + DEPENDEES install + WORKING_DIRECTORY + LOG 1 + ) + elseif(NOT ANDROID) # FIXME need to account for different architectures diff --git a/cmake/modules/FindSixense.cmake b/cmake/modules/FindSixense.cmake index 9abacac136..5a94d592d4 100644 --- a/cmake/modules/FindSixense.cmake +++ b/cmake/modules/FindSixense.cmake @@ -18,6 +18,10 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # +include(SelectLibraryConfigurations) +select_library_configurations(SIXENSE) + +set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 2527da9e03..6cb58ced82 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include @@ -74,7 +76,13 @@ SixenseManager::SixenseManager() : bool SixenseManager::isSupported() const { #ifdef HAVE_SIXENSE + +#if defined(Q_OS_OSX) + return QSysInfo::macVersion() <= QSysInfo::MV_MAVERICKS; +#else return true; +#endif + #else return false; #endif From 0355a37fb147d8bc7b4dcc3a28129d4343edd3d8 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Fri, 30 Oct 2015 22:19:44 -0700 Subject: [PATCH 0584/1003] Fixing SDL2 active/inactive value --- libraries/input-plugins/src/input-plugins/SDL2Manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index 600dc5c56f..48864beedc 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -82,6 +82,7 @@ void SDL2Manager::activate() { emit joystickAdded(joystick.get()); } #endif + InputPlugin::activate(); } void SDL2Manager::deactivate() { @@ -92,6 +93,7 @@ void SDL2Manager::deactivate() { emit joystickRemoved(joystick.get()); } #endif + InputPlugin::deactivate(); } From aee03e79f390a6b60a474eedb9bcf4ecfd3a5e50 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 30 Oct 2015 22:37:00 -0700 Subject: [PATCH 0585/1003] Fix win32 and linux Sixense config --- cmake/externals/sixense/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index 5edccb2c88..07cf1c1163 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -30,7 +30,7 @@ if (WIN32) set(ARCH_SUFFIX "") endif() - set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll") elseif(APPLE) @@ -63,7 +63,7 @@ elseif(APPLE) elseif(NOT ANDROID) # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) endif() From 224aeea04406d56ae901e14d7d1216c34ff592b6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 31 Oct 2015 08:11:43 -0700 Subject: [PATCH 0586/1003] quiet compiler --- libraries/animation/src/AnimVariant.cpp | 2 +- libraries/controllers/src/controllers/UserInputMapper.cpp | 4 ++-- libraries/controllers/src/controllers/impl/Endpoint.h | 3 ++- .../src/controllers/impl/conditionals/EndpointConditional.h | 2 +- .../controllers/src/controllers/impl/endpoints/AnyEndpoint.h | 1 + .../src/controllers/impl/endpoints/ArrayEndpoint.h | 1 + .../src/controllers/impl/endpoints/CompositeEndpoint.h | 1 + .../controllers/src/controllers/impl/endpoints/JSEndpoint.h | 1 + .../src/controllers/impl/endpoints/ScriptEndpoint.h | 1 + .../src/controllers/impl/endpoints/StandardEndpoint.h | 2 +- 10 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 8d320195dd..234e9cef09 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -44,7 +44,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, 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"); + assert(QString("AnimVariant::Type") == QString("valid")); } }; if (useNames) { // copy only the requested names diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index d33e215797..8e121de7fb 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -74,7 +74,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) { } const auto& deviceID = device->_deviceID; - int numberOfType = recordDeviceOfType(device->getName()); + recordDeviceOfType(device->getName()); qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID; for (const auto& inputMapping : device->getAvailableInputs()) { @@ -266,7 +266,7 @@ void UserInputMapper::update(float deltaTime) { } auto standardInputs = getStandardInputs(); - if (_lastStandardStates.size() != standardInputs.size()) { + if ((int)_lastStandardStates.size() != standardInputs.size()) { _lastStandardStates.resize(standardInputs.size()); for (auto& lastValue : _lastStandardStates) { lastValue = 0; diff --git a/libraries/controllers/src/controllers/impl/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h index 5dd3f6adb4..bc604da2c5 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -40,7 +40,7 @@ namespace controller { virtual void apply(float value, const Pointer& source) = 0; virtual Pose pose() { return Pose(); } virtual void apply(const Pose& value, const Pointer& source) {} - virtual const bool isPose() { return _input.isPose(); } + virtual bool isPose() { return _input.isPose(); } virtual bool writeable() const { return true; } virtual bool readable() const { return true; } @@ -54,6 +54,7 @@ namespace controller { class LambdaEndpoint : public Endpoint { public: + using Endpoint::apply; LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {}) : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 1e4205afc7..54ed57e871 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -18,7 +18,7 @@ namespace controller { class EndpointConditional : public Conditional { public: EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} - virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; } + virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0f; } private: Endpoint::Pointer _endpoint; }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h index 86dd057414..24834ce223 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h @@ -17,6 +17,7 @@ namespace controller { class AnyEndpoint : public Endpoint { friend class UserInputMapper; public: + using Endpoint::apply; AnyEndpoint(Endpoint::List children); virtual float value() override; virtual void apply(float newValue, const Endpoint::Pointer& source) override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h index 79bb604ec0..899fa46de0 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h @@ -17,6 +17,7 @@ namespace controller { class ArrayEndpoint : public Endpoint { friend class UserInputMapper; public: + using Endpoint::apply; using Pointer = std::shared_ptr; ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { } diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h index c6ec90b7c8..b29266464c 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h @@ -15,6 +15,7 @@ namespace controller { class CompositeEndpoint : public Endpoint, Endpoint::Pair { public: + using Endpoint::apply; CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second); virtual float value() override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h index 27f17b5cd3..5113fef657 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -19,6 +19,7 @@ namespace controller { class JSEndpoint : public Endpoint { public: + using Endpoint::apply; JSEndpoint(const QJSValue& callable) : Endpoint(Input::INVALID_INPUT), _callable(callable) { } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index 23f77892c6..37160fcb48 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -19,6 +19,7 @@ namespace controller { class ScriptEndpoint : public Endpoint { Q_OBJECT; public: + using Endpoint::apply; ScriptEndpoint(const QScriptValue& callable) : Endpoint(Input::INVALID_INPUT), _callable(callable) { } diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h index 74adaf825d..7fe1a5467e 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h @@ -32,7 +32,7 @@ public: virtual void apply(float value, const Pointer& source) override { // For standard endpoints, the first NON-ZERO write counts. - if (value != 0.0) { + if (value != 0.0f) { _written = true; } VirtualEndpoint::apply(value, source); From 5916875345dcd51d16cfc484c5762295f40feac2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 31 Oct 2015 08:20:24 -0700 Subject: [PATCH 0587/1003] one more --- .../controllers/src/controllers/impl/filters/InvertFilter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h index 889cd0140c..8acc9d56d6 100644 --- a/libraries/controllers/src/controllers/impl/filters/InvertFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.h @@ -17,6 +17,7 @@ namespace controller { class InvertFilter : public ScaleFilter { REGISTER_FILTER_CLASS(InvertFilter); public: + using ScaleFilter::parseParameters; InvertFilter() : ScaleFilter(-1.0f) {} virtual bool parseParameters(const QJsonArray& parameters) { return true; } From b877f832a9d7175826ad581c0ca50abe044a5936 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 31 Oct 2015 13:02:17 -0700 Subject: [PATCH 0588/1003] fix SDL joysticks --- .../src/controllers/StandardControls.h | 6 ++--- .../src/input-plugins/SDL2Manager.cpp | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index 9c6defb865..d5063f6034 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -58,15 +58,15 @@ namespace controller { // Left Analog stick LX = 0, LY, - LZ, // Right Analog stick RX, RY, - RZ, // Triggers LT, RT, - NUM_STANDARD_AXES + NUM_STANDARD_AXES, + LZ = LT, + RZ = RT }; // No correlation to SDL diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index 48864beedc..ec2fa2ed07 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -18,6 +18,33 @@ #include "SDL2Manager.h" +#ifdef HAVE_SDL2 +static_assert( + controller::A == SDL_CONTROLLER_BUTTON_A && + controller::B == SDL_CONTROLLER_BUTTON_B && + controller::X == SDL_CONTROLLER_BUTTON_X && + controller::Y == SDL_CONTROLLER_BUTTON_Y && + controller::BACK == SDL_CONTROLLER_BUTTON_BACK && + controller::GUIDE == SDL_CONTROLLER_BUTTON_GUIDE && + controller::START == SDL_CONTROLLER_BUTTON_START && + controller::LS == SDL_CONTROLLER_BUTTON_LEFTSTICK && + controller::RS == SDL_CONTROLLER_BUTTON_RIGHTSTICK && + controller::LB == SDL_CONTROLLER_BUTTON_LEFTSHOULDER && + controller::RB == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER && + controller::DU == SDL_CONTROLLER_BUTTON_DPAD_UP && + controller::DD == SDL_CONTROLLER_BUTTON_DPAD_DOWN && + controller::DL == SDL_CONTROLLER_BUTTON_DPAD_LEFT && + controller::DR == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && + controller::LX == SDL_CONTROLLER_AXIS_LEFTX && + controller::LY == SDL_CONTROLLER_AXIS_LEFTY && + controller::RX == SDL_CONTROLLER_AXIS_RIGHTX && + controller::RY == SDL_CONTROLLER_AXIS_RIGHTY && + controller::LT == SDL_CONTROLLER_AXIS_TRIGGERLEFT && + controller::RT == SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + "SDL2 equvalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h"); +#endif + + const QString SDL2Manager::NAME = "SDL2"; #ifdef HAVE_SDL2 From 46f9a432aba2778a0060ae2ba0935effc8e83bf2 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 31 Oct 2015 14:21:50 -0700 Subject: [PATCH 0589/1003] fix handControllerGrab.js --- examples/controllers/handControllerGrab.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 60e5286ad8..727e42cdc1 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -154,7 +154,7 @@ function entityIsGrabbedByOther(entityID) { } -function MyController(hand, triggerAction) { +function MyController(hand) { this.hand = hand; if (this.hand === RIGHT_HAND) { this.getHandPosition = MyAvatar.getRightPalmPosition; @@ -166,7 +166,6 @@ function MyController(hand, triggerAction) { var SPATIAL_CONTROLLERS_PER_PALM = 2; var TIP_CONTROLLER_OFFSET = 1; - this.triggerAction = triggerAction; this.palm = SPATIAL_CONTROLLERS_PER_PALM * hand; this.tip = SPATIAL_CONTROLLERS_PER_PALM * hand + TIP_CONTROLLER_OFFSET; @@ -282,7 +281,6 @@ function MyController(hand, triggerAction) { // smooth out trigger value this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); - }; this.triggerSmoothedSqueezed = function() { @@ -439,8 +437,7 @@ function MyController(hand, triggerAction) { 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 grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); var now = Date.now(); @@ -906,8 +903,8 @@ function MyController(hand, triggerAction) { }; } -var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); -var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); +var rightController = new MyController(RIGHT_HAND); +var leftController = new MyController(LEFT_HAND); var MAPPING_NAME = "com.highfidelity.handControllerGrab"; From abbfe153959cbe51db4b18b409903da7cfadc2f7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 30 Oct 2015 14:34:18 -0700 Subject: [PATCH 0590/1003] Adding peek support for endpoints --- examples/controllers/handControllerGrab.js | 4 ++-- .../src/controllers/UserInputMapper.cpp | 16 +++++++++------- .../src/controllers/UserInputMapper.h | 8 ++++---- .../controllers/src/controllers/impl/Endpoint.h | 15 ++++++++------- .../controllers/src/controllers/impl/Route.h | 1 + .../src/controllers/impl/RouteBuilderProxy.cpp | 5 +++++ .../src/controllers/impl/RouteBuilderProxy.h | 3 ++- .../impl/conditionals/EndpointConditional.h | 2 +- .../controllers/impl/endpoints/ActionEndpoint.h | 4 ++-- .../controllers/impl/endpoints/AnyEndpoint.cpp | 11 +++++++++++ .../controllers/impl/endpoints/AnyEndpoint.h | 1 + .../controllers/impl/endpoints/ArrayEndpoint.h | 4 +--- .../impl/endpoints/CompositeEndpoint.cpp | 6 ++++++ .../impl/endpoints/CompositeEndpoint.h | 1 + .../impl/endpoints/InputEndpoint.cpp | 17 +++++++++++++---- .../controllers/impl/endpoints/InputEndpoint.h | 2 ++ .../src/controllers/impl/endpoints/JSEndpoint.h | 5 ++--- .../impl/endpoints/ScriptEndpoint.cpp | 4 ++-- .../controllers/impl/endpoints/ScriptEndpoint.h | 4 ++-- 19 files changed, 75 insertions(+), 38 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 727e42cdc1..a866983194 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -909,8 +909,8 @@ var leftController = new MyController(LEFT_HAND); 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); +mapping.from([Controller.Standard.RB, Controller.Standard.RT]).peek().to(rightController.eitherTrigger); +mapping.from([Controller.Standard.LB, Controller.Standard.LT]).peek().to(leftController.eitherTrigger); Controller.enableMapping(MAPPING_NAME); diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 8e121de7fb..1a0fae8158 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -513,7 +513,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { // 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->peek && !source->readable()) { if (debugRoutes && route->debug) { qCDebug(controllers) << "Source unreadable"; } @@ -539,7 +539,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { // Fetch the value, may have been overriden by previous loopback routes if (source->isPose()) { - Pose value = getPose(source); + Pose value = getPose(source, route->peek); static const Pose IDENTITY_POSE { vec3(), quat() }; if (debugRoutes && route->debug) { if (!value.valid) { @@ -554,7 +554,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { destination->apply(value, source); } else { // Fetch the value, may have been overriden by previous loopback routes - float value = getValue(source); + float value = getValue(source, route->peek); if (debugRoutes && route->debug) { qCDebug(controllers) << "Value was " << value; @@ -691,8 +691,8 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { } } -float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) { - return endpoint->value(); +float UserInputMapper::getValue(const Endpoint::Pointer& endpoint, bool peek) { + return peek ? endpoint->peek() : endpoint->value(); } float UserInputMapper::getValue(const Input& input) const { @@ -704,11 +704,11 @@ float UserInputMapper::getValue(const Input& input) const { return endpoint->value(); } -Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) { +Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint, bool peek) { if (!endpoint->isPose()) { return Pose(); } - return endpoint->pose(); + return peek ? endpoint->peekPose() : endpoint->pose(); } Pose UserInputMapper::getPose(const Input& input) const { @@ -742,6 +742,7 @@ 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_PEEK = QStringLiteral("peek"); static const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); static const QString JSON_CHANNEL_TO = QStringLiteral("to"); static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); @@ -953,6 +954,7 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { result->json = QString(QJsonDocument(obj).toJson()); result->source = parseSource(obj[JSON_CHANNEL_FROM]); result->debug = obj[JSON_CHANNEL_DEBUG].toBool(); + result->debug = obj[JSON_CHANNEL_PEEK].toBool(); if (!result->source) { qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; return Route::Pointer(); diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 7684ecb7c5..c1dfcf5d33 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -38,8 +38,8 @@ namespace controller { class UserInputMapper : public QObject, public Dependency { Q_OBJECT - SINGLETON_DEPENDENCY - Q_ENUMS(Action) + SINGLETON_DEPENDENCY + Q_ENUMS(Action) public: // FIXME move to unordered set / map @@ -135,8 +135,8 @@ namespace controller { int recordDeviceOfType(const QString& deviceName); QHash _deviceCounts; - static float getValue(const EndpointPointer& endpoint); - static Pose getPose(const EndpointPointer& endpoint); + static float getValue(const EndpointPointer& endpoint, bool peek = false); + static Pose getPose(const EndpointPointer& endpoint, bool peek = false); friend class RouteBuilderProxy; friend class MappingBuilderProxy; diff --git a/libraries/controllers/src/controllers/impl/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h index bc604da2c5..475dc035bb 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -36,12 +36,13 @@ namespace controller { using WriteLambda = std::function; Endpoint(const Input& input) : _input(input) {} - virtual float value() = 0; + virtual float value() { return peek(); } + virtual float peek() const = 0; virtual void apply(float value, const Pointer& source) = 0; - virtual Pose pose() { return Pose(); } + virtual Pose peekPose() const { return Pose(); }; + virtual Pose pose() { return peekPose(); } virtual void apply(const Pose& value, const Pointer& source) {} - virtual bool isPose() { return _input.isPose(); } - + virtual bool isPose() const { return _input.isPose(); } virtual bool writeable() const { return true; } virtual bool readable() const { return true; } virtual void reset() { } @@ -58,7 +59,7 @@ namespace controller { LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {}) : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } - virtual float value() override { return _readLambda(); } + virtual float peek() const override { return _readLambda(); } virtual void apply(float value, const Pointer& source) override { _writeLambda(value); } private: @@ -73,10 +74,10 @@ namespace controller { : Endpoint(id) { } - virtual float value() override { return _currentValue; } + virtual float peek() const override { return _currentValue; } virtual void apply(float value, const Pointer& source) override { _currentValue = value; } - virtual Pose pose() override { return _currentPose; } + virtual Pose peekPose() const override { return _currentPose; } virtual void apply(const Pose& value, const Pointer& source) override { _currentPose = value; } diff --git a/libraries/controllers/src/controllers/impl/Route.h b/libraries/controllers/src/controllers/impl/Route.h index 5ad3d36628..554dd82d5a 100644 --- a/libraries/controllers/src/controllers/impl/Route.h +++ b/libraries/controllers/src/controllers/impl/Route.h @@ -26,6 +26,7 @@ namespace controller { Filter::List filters; QString json; bool debug { false }; + bool peek { false }; using Pointer = std::shared_ptr; using List = std::list; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 49e615439d..5ae52893e0 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -52,6 +52,11 @@ QObject* RouteBuilderProxy::debug(bool enable) { return this; } +QObject* RouteBuilderProxy::peek(bool enable) { + _route->peek = enable; + return this; +} + QObject* RouteBuilderProxy::when(const QScriptValue& expression) { _route->conditional = _parent.conditionalFor(expression); return this; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index d55aa80f6b..1c0ed6931d 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -38,6 +38,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE void to(const QScriptValue& destination); Q_INVOKABLE QObject* debug(bool enable = true); + Q_INVOKABLE QObject* peek(bool enable = true); Q_INVOKABLE QObject* when(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); Q_INVOKABLE QObject* hysteresis(float min, float max); @@ -48,7 +49,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* constrainToInteger(); Q_INVOKABLE QObject* constrainToPositiveInteger(); -private: + private: void to(const Endpoint::Pointer& destination); void conditional(const Conditional::Pointer& conditional); void addFilter(Filter::Pointer filter); diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 54ed57e871..0ba1347087 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -18,7 +18,7 @@ namespace controller { class EndpointConditional : public Conditional { public: EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} - virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0f; } + virtual bool satisfied() override { return _endpoint && _endpoint->peek() != 0.0f; } private: Endpoint::Pointer _endpoint; }; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h index 574fdcedb5..e07dc9e4c8 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h @@ -23,10 +23,10 @@ class ActionEndpoint : public Endpoint { public: ActionEndpoint(const Input& id = Input::INVALID_INPUT) : Endpoint(id) { } - virtual float value() override { return _currentValue; } + virtual float peek() const { return _currentValue; } virtual void apply(float newValue, const Pointer& source) override; - virtual Pose pose() override { return _currentPose; } + virtual Pose peekPose() const { return _currentPose; } 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 24f3479ea8..0dd53fe78f 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp @@ -27,6 +27,17 @@ AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPU } } +float AnyEndpoint::peek() const { + for (auto& child : _children) { + float childResult = child->peek(); + if (childResult != 0.0f) { + return childResult; + } + } + return 0.0f; +} + +// Fetching the value must trigger any necessary side effects of value() on ALL the children. float AnyEndpoint::value() { float result = 0; for (auto& child : _children) { diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h index 24834ce223..8947eb675f 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h @@ -19,6 +19,7 @@ class AnyEndpoint : public Endpoint { public: using Endpoint::apply; AnyEndpoint(Endpoint::List children); + virtual float peek() const override; virtual float value() override; virtual void apply(float newValue, const Endpoint::Pointer& source) override; virtual bool writeable() const override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h index 899fa46de0..34d30a2e97 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h @@ -21,9 +21,7 @@ public: using Pointer = std::shared_ptr; ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { } - virtual float value() override { - return 0.0; - } + virtual float peek() const override { return 0.0f; } virtual void apply(float value, const Endpoint::Pointer& source) override { for (auto& child : _children) { diff --git a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp index 913bf0136b..1bd27489f8 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp @@ -24,6 +24,12 @@ bool CompositeEndpoint::readable() const { return first->readable() && second->readable(); } +float CompositeEndpoint::peek() const { + float result = first->peek() * -1.0f + second->peek(); + return result; +} + +// Fetching via value() must trigger any side effects of value() on the children 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 b29266464c..3249aa1d37 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h @@ -18,6 +18,7 @@ namespace controller { using Endpoint::apply; CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second); + virtual float peek() const override; virtual float value() override; virtual void apply(float newValue, const Pointer& source) override; virtual bool readable() const override; diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp index bb1f6df191..ce58c948d1 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp @@ -13,10 +13,10 @@ #include "../../UserInputMapper.h" using namespace controller; -float InputEndpoint::value(){ - _read = true; + +float InputEndpoint::peek() const { if (isPose()) { - return pose().valid ? 1.0f : 0.0f; + return peekPose().valid ? 1.0f : 0.0f; } auto userInputMapper = DependencyManager::get(); auto deviceProxy = userInputMapper->getDevice(_input); @@ -26,8 +26,12 @@ float InputEndpoint::value(){ return deviceProxy->getValue(_input); } -Pose InputEndpoint::pose() { +float InputEndpoint::value(){ _read = true; + return peek(); +} + +Pose InputEndpoint::peekPose() const { if (!isPose()) { return Pose(); } @@ -39,3 +43,8 @@ Pose InputEndpoint::pose() { return deviceProxy->getPose(_input.channel); } +Pose InputEndpoint::pose() { + _read = true; + return peekPose(); +} + diff --git a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h index d58f0c2e73..663168bedc 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h @@ -20,9 +20,11 @@ public: : Endpoint(id) { } + virtual float peek() const override; virtual float value() override; // FIXME need support for writing back to vibration / force feedback effects virtual void apply(float newValue, const Pointer& source) override {} + virtual Pose peekPose() const override; virtual Pose pose() override; virtual void apply(const Pose& value, const Pointer& source) override { } diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h index 5113fef657..958914264e 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h @@ -24,9 +24,8 @@ public: : Endpoint(Input::INVALID_INPUT), _callable(callable) { } - virtual float value() { - float result = (float)_callable.call().toNumber(); - return result; + virtual float peek() const { + return (float)const_cast(this)->_callable.call().toNumber(); } virtual void apply(float newValue, const Pointer& source) { diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp index d9b4a5fc59..bb9517b136 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp @@ -12,8 +12,8 @@ using namespace controller; -float ScriptEndpoint::value() { - updateValue(); +float ScriptEndpoint::peek() const { + const_cast(this)->updateValue(); return _lastValueRead; } diff --git a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h index 37160fcb48..836af721f6 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +++ b/libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h @@ -24,8 +24,8 @@ public: : Endpoint(Input::INVALID_INPUT), _callable(callable) { } - virtual float value(); - virtual void apply(float newValue, const Pointer& source); + virtual float peek() const override; + virtual void apply(float newValue, const Pointer& source) override; protected: Q_INVOKABLE void updateValue(); From 0e13348b5a3f4874552097cff8f58a122be72676 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 31 Oct 2015 13:24:26 -0700 Subject: [PATCH 0591/1003] PR feedback --- .../src/controllers/impl/endpoints/AnyEndpoint.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp index 0dd53fe78f..a3b719b2c1 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp @@ -28,23 +28,18 @@ AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPU } float AnyEndpoint::peek() const { + float result = 0; for (auto& child : _children) { - float childResult = child->peek(); - if (childResult != 0.0f) { - return childResult; - } + result = std::max(result, child->peek()); } - return 0.0f; + return result; } // Fetching the value must trigger any necessary side effects of value() on ALL the children. float AnyEndpoint::value() { float result = 0; for (auto& child : _children) { - float childResult = child->value(); - if (childResult != 0.0f) { - result = childResult; - } + result = std::max(result, child->value()); } return result; } From 9346366fc51e79cc1515ec63166ec0dc9f912824 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 31 Oct 2015 14:28:54 -0700 Subject: [PATCH 0592/1003] fix grab script --- examples/controllers/handControllerGrab.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 727e42cdc1..339c1ad002 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -437,8 +437,9 @@ function MyController(hand) { 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"]); - + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", + "gravity", "ignoreForCollisions", + "collisionsWillMove"]); var now = Date.now(); // add the action and initialize some variables From 05dea847be1e2029157e6355fccf9df8794f0f7a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 31 Oct 2015 15:47:40 -0700 Subject: [PATCH 0593/1003] add Actions.CycleCamera and wire up secondary thumb buttons to Actions.CycleCamera and Actions.ContextMenu --- interface/resources/controllers/standard.json | 3 + interface/src/Application.cpp | 32 ++++++++- interface/src/Application.h | 3 +- .../controllers/src/controllers/Actions.cpp | 1 + .../controllers/src/controllers/Actions.h | 1 + .../src/controllers/StandardController.cpp | 36 ---------- .../src/input-plugins/KeyboardMouseDevice.cpp | 69 ------------------- .../src/input-plugins/SixenseManager.cpp | 43 ------------ 8 files changed, 37 insertions(+), 151 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 871374b85b..dff0ab95b0 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -25,6 +25,9 @@ { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, + { "from": "Standard.LeftSecondaryThumb", "to": "Actions.CycleCamera" }, + { "from": "Standard.RightSecondaryThumb", "to": "Actions.ContextMenu" }, + { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7f4b5a3c3d..dc2eee000d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -630,8 +630,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Setup the userInputMapper with the actions auto userInputMapper = DependencyManager::get(); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { - if (state && action == toInt(controller::Action::TOGGLE_MUTE)) { - DependencyManager::get()->toggleMute(); + if (state) { + if (action == controller::toInt(controller::Action::TOGGLE_MUTE)) { + DependencyManager::get()->toggleMute(); + } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) { + cycleCamera(); + } } }); @@ -2620,6 +2624,30 @@ void Application::updateThreads(float deltaTime) { } } +void Application::cycleCamera() { + auto menu = Menu::getInstance(); + if (menu->isOptionChecked(MenuOption::FullscreenMirror)) { + + menu->setIsOptionChecked(MenuOption::FullscreenMirror, false); + menu->setIsOptionChecked(MenuOption::FirstPerson, true); + + } else if (menu->isOptionChecked(MenuOption::FirstPerson)) { + + menu->setIsOptionChecked(MenuOption::FirstPerson, false); + menu->setIsOptionChecked(MenuOption::ThirdPerson, true); + + } else if (menu->isOptionChecked(MenuOption::ThirdPerson)) { + + menu->setIsOptionChecked(MenuOption::ThirdPerson, false); + menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); + + } else if (menu->isOptionChecked(MenuOption::IndependentMode)) { + // do nothing if in independe mode + return; + } + cameraMenuChanged(); // handle the menu change +} + void Application::cameraMenuChanged() { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { diff --git a/interface/src/Application.h b/interface/src/Application.h index bf45a0c6ec..dfc904e0ef 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -293,8 +293,9 @@ public slots: void aboutApp(); void showEditEntitiesHelp(); + void cycleCamera(); void cameraMenuChanged(); - + void reloadResourceCaches(); void crashApplication(); diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index a4a8656fb7..701aea79c5 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -60,6 +60,7 @@ namespace controller { makeButtonPair(Action::ACTION2, "SecondaryAction"), makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), + makeButtonPair(Action::CYCLE_CAMERA, "CycleCamera"), // Aliases and bisected versions makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 36df695032..d5ad44c720 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -51,6 +51,7 @@ enum class Action { CONTEXT_MENU, TOGGLE_MUTE, + CYCLE_CAMERA, SHIFT, diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 44f1bff1ae..31c023b1ea 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -118,41 +118,5 @@ QString StandardController::getDefaultMappingConfig() const { return DEFAULT_MAPPING_JSON; } -// 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)); -//} } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index a0481dfaa0..9cd510c521 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -209,72 +209,3 @@ QString KeyboardMouseDevice::getDefaultMappingConfig() const { 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/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 6cb58ced82..5e0f3277b1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -556,49 +556,6 @@ QString SixenseManager::getDefaultMappingConfig() const { 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; From 77816d818a096b3d331b857b602a3074e22cd453 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sat, 31 Oct 2015 16:00:22 -0700 Subject: [PATCH 0594/1003] make RY map to jump --- 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 dff0ab95b0..b08abcbaad 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -16,7 +16,7 @@ { "from": "Standard.RX", "to": "Actions.Yaw" }, - { "from": "Standard.RY", "to": "Actions.Pitch" }, + { "from": "Standard.RY", "filters": "invert", "to": "Actions.TranslateY" }, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, From 7fa6fb13085793560eca7b153aacf63e4b4e0f68 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 1 Nov 2015 01:36:12 -0700 Subject: [PATCH 0595/1003] Remove GLEW spam in cmake --- cmake/modules/FindGLEW.cmake | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake index 4f22271e3e..f4eca0eddf 100644 --- a/cmake/modules/FindGLEW.cmake +++ b/cmake/modules/FindGLEW.cmake @@ -30,6 +30,4 @@ include(SelectLibraryConfigurations) select_library_configurations(GLEW) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES) - -message(STATUS "Found GLEW - Assuming that GLEW is static and defining GLEW_STATIC") \ No newline at end of file +find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_INCLUDE_DIRS GLEW_LIBRARIES) \ No newline at end of file From 99d8554cc8050fa29ba73205155e7d455f0d2458 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 1 Nov 2015 01:36:54 -0700 Subject: [PATCH 0596/1003] Make zlib a true external on windows (linux and OSX should rely on system libraries) --- cmake/externals/zlib/CMakeLists.txt | 49 +++++++++++++++-------------- cmake/macros/TargetZlib.cmake | 17 ++++++++++ libraries/shared/CMakeLists.txt | 12 +------ 3 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 cmake/macros/TargetZlib.cmake diff --git a/cmake/externals/zlib/CMakeLists.txt b/cmake/externals/zlib/CMakeLists.txt index 69dbca714d..0d279cc469 100644 --- a/cmake/externals/zlib/CMakeLists.txt +++ b/cmake/externals/zlib/CMakeLists.txt @@ -1,28 +1,31 @@ +set(EXTERNAL_NAME zlib) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (WIN32) - set(EXTERNAL_NAME zlib) - string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +include(ExternalProject) - include(ExternalProject) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://zlib.net/zlib128.zip - URL_MD5 126f8676442ffbd97884eb4d6f32afb4 - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) +ExternalProject_Add( +${EXTERNAL_NAME} +URL http://zlib.net/zlib128.zip +CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= +BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build +LOG_DOWNLOAD 1 +LOG_CONFIGURE 1 +LOG_BUILD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - # Hide this external target (for ide users) - set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of zlib include directories") +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of zlib include directories") +set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/bin CACHE FILEPATH "Location of ZLib DLL") +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/zlib.lib CACHE FILEPATH "Location of zlib release library") +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/zlibd.lib CACHE FILEPATH "Location of zlib debug library") - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) +include(SelectLibraryConfigurations) +select_library_configurations(${EXTERNAL_NAME_UPPER}) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "List of zlib include directories") - - ExternalProject_Get_Property(${EXTERNAL_NAME} BINARY_DIR) - - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${BINARY_DIR}/Release CACHE FILEPATH "Location of GLEW DLL") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${BINARY_DIR}/Release/zlib.lib CACHE FILEPATH "Location of ZLib release library") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Location of ZLib debug library") - -endif () \ No newline at end of file +# Force selected libraries into the cache +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of zlib libraries") +set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of zlib libraries") diff --git a/cmake/macros/TargetZlib.cmake b/cmake/macros/TargetZlib.cmake new file mode 100644 index 0000000000..11eb8c132e --- /dev/null +++ b/cmake/macros/TargetZlib.cmake @@ -0,0 +1,17 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_ZLIB) + if (WIN32) + add_dependency_external_projects(zlib) + add_paths_to_fixup_libs(${ZLIB_DLL_PATH}) + else() + find_package(ZLIB REQUIRED) + endif() + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) +endmacro() diff --git a/libraries/shared/CMakeLists.txt b/libraries/shared/CMakeLists.txt index d9df5eba7f..2691c51128 100644 --- a/libraries/shared/CMakeLists.txt +++ b/libraries/shared/CMakeLists.txt @@ -3,14 +3,4 @@ set(TARGET_NAME shared) # TODO: there isn't really a good reason to have Script linked here - let's get what is requiring it out (RegisteredMetaTypes.cpp) setup_hifi_library(Gui Network Script Widgets) -find_package(ZLIB REQUIRED) -target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) -target_include_directories(${TARGET_NAME} PUBLIC ${ZLIB_INCLUDE_DIRS}) - -if (WIN32) - # Birarda will fix this when he finds it. - get_filename_component(ZLIB_LIB_DIR "${ZLIB_LIBRARIES}" DIRECTORY) - get_filename_component(ZLIB_DIR "${ZLIB_LIB_DIR}" DIRECTORY) - set(ZLIB_BIN_DIR "${ZLIB_DIR}/bin") - add_paths_to_fixup_libs(${ZLIB_BIN_DIR}) -endif () +target_zlib() From cad1d28b7bee0af3474c06cea1ae1fed4e750af7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 1 Nov 2015 01:49:33 -0700 Subject: [PATCH 0597/1003] Updating build notes --- BUILD.md | 6 +----- BUILD_WIN.md | 10 ---------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/BUILD.md b/BUILD.md index e2f310bb1f..7ad2ddeee0 100644 --- a/BUILD.md +++ b/BUILD.md @@ -13,17 +13,13 @@ * [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4 * [gverb](https://github.com/highfidelity/gverb) - -The following external projects are optional dependencies. You can indicate to CMake that you would like to include them by passing -DGET_$NAME=1 when running a clean CMake build. For example, to get CMake to download and compile SDL2 you would pass -DGET_SDL2=1. - * [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3 - * Enables game controller support in Interface The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project. These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder. -If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DGET_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project. +If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project. ###OS Specific Build Guides * [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index d291cef4f2..89646e99ff 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -75,16 +75,6 @@ To prevent these problems, install OpenSSL yourself. Download the following bina Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version. -####zlib - -Install zlib from - - [Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm) - -and fix a header file, as described here: - - [zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/) - ###Build High Fidelity using Visual Studio Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. From e899229cdeb0f78d274ad4b83c91585b778d37c1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 1 Nov 2015 09:55:30 -0800 Subject: [PATCH 0598/1003] tweaks to how VrMenu is shown --- interface/resources/controllers/keyboardMouse.json | 3 ++- interface/src/Application.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8af6b1dc98..9c3ba79d76 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -81,6 +81,7 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, - { "from": "Keyboard.T", "to": "Actions.ACTION2" } + { "from": "Keyboard.T", "to": "Actions.ACTION2" }, + { "from": "Keyboard.RightMouseClick", "to": "Actions.ContextMenu" } ] } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc2eee000d..22f61a12e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -635,6 +635,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : DependencyManager::get()->toggleMute(); } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) { cycleCamera(); + } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { + VrMenu::toggle(); // show context menu even on non-stereo displays } } }); @@ -1789,9 +1791,7 @@ void Application::keyPressEvent(QKeyEvent* event) { void Application::keyReleaseEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Alt && _altPressed && hasFocus()) { - if (getActiveDisplayPlugin()->isStereo()) { - VrMenu::toggle(); - } + VrMenu::toggle(); // show context menu even on non-stereo displays } _keysPressed.remove(event->key()); From cfb2fd1523c8c7ce027a720c4c5abe157fa788ef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 27 Oct 2015 11:52:47 -0700 Subject: [PATCH 0599/1003] Support for runtime plugins (DLLs) --- CMakeLists.txt | 1 + cmake/externals/glew/CMakeLists.txt | 5 +- cmake/macros/SetupHifiPlugin.cmake | 33 +++++ interface/src/Application.cpp | 1 - interface/src/PluginContainerProxy.cpp | 1 - libraries/display-plugins/CMakeLists.txt | 7 +- .../AbstractHMDScriptingInterface.cpp | 5 +- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 22 +-- .../src/display-plugins/DisplayPlugin.cpp | 12 -- .../src/display-plugins/DisplayPlugin.h | 131 +--------------- .../src/display-plugins/NullDisplayPlugin.cpp | 2 +- .../src/display-plugins/NullDisplayPlugin.h | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 1 + .../WindowOpenGLDisplayPlugin.cpp | 2 +- .../openvr/OpenVrDisplayPlugin.cpp | 4 +- .../stereo/StereoDisplayPlugin.cpp | 10 +- .../src/input-plugins/InputPlugin.h | 12 +- .../src/input-plugins/SixenseManager.cpp | 10 +- .../input-plugins/ViveControllerManager.cpp | 8 +- libraries/plugins/src/plugins/DisplayPlugin.h | 140 ++++++++++++++++++ libraries/plugins/src/plugins/Forward.h | 1 + libraries/plugins/src/plugins/InputPlugin.h | 23 +++ libraries/plugins/src/plugins/Plugin.cpp | 4 +- libraries/plugins/src/plugins/Plugin.h | 6 +- .../plugins/src/plugins/PluginManager.cpp | 73 ++++++++- libraries/plugins/src/plugins/PluginManager.h | 1 + libraries/plugins/src/plugins/RuntimePlugin.h | 36 +++++ plugins/CMakeLists.txt | 18 +++ plugins/oculus/CMakeLists.txt | 22 +++ .../oculus/src}/OculusBaseDisplayPlugin.cpp | 8 + .../oculus/src}/OculusBaseDisplayPlugin.h | 3 +- .../oculus/src}/OculusDebugDisplayPlugin.cpp | 0 .../oculus/src}/OculusDebugDisplayPlugin.h | 0 .../oculus/src}/OculusDisplayPlugin.cpp | 13 +- .../oculus/src}/OculusDisplayPlugin.h | 0 .../oculus/src}/OculusHelpers.cpp | 0 .../oculus/src}/OculusHelpers.h | 0 plugins/oculus/src/OculusProvider.cpp | 54 +++++++ plugins/oculus/src/oculus.json | 1 + plugins/oculusLegacy/CMakeLists.txt | 22 +++ plugins/oculusLegacy/src/OculusHelpers.cpp | 9 ++ plugins/oculusLegacy/src/OculusHelpers.h | 85 +++++++++++ .../src}/OculusLegacyDisplayPlugin.cpp | 34 +---- .../src}/OculusLegacyDisplayPlugin.h | 2 +- plugins/oculusLegacy/src/OculusProvider.cpp | 45 ++++++ plugins/oculusLegacy/src/oculus.json | 1 + tests/controllers/src/main.cpp | 3 - 47 files changed, 631 insertions(+), 242 deletions(-) create mode 100644 cmake/macros/SetupHifiPlugin.cmake create mode 100644 libraries/plugins/src/plugins/DisplayPlugin.h create mode 100644 libraries/plugins/src/plugins/InputPlugin.h create mode 100644 libraries/plugins/src/plugins/RuntimePlugin.h create mode 100644 plugins/CMakeLists.txt create mode 100644 plugins/oculus/CMakeLists.txt rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusBaseDisplayPlugin.cpp (95%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusBaseDisplayPlugin.h (96%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusDebugDisplayPlugin.cpp (100%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusDebugDisplayPlugin.h (100%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusDisplayPlugin.cpp (97%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusDisplayPlugin.h (100%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusHelpers.cpp (100%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculus/src}/OculusHelpers.h (100%) create mode 100644 plugins/oculus/src/OculusProvider.cpp create mode 100644 plugins/oculus/src/oculus.json create mode 100644 plugins/oculusLegacy/CMakeLists.txt create mode 100644 plugins/oculusLegacy/src/OculusHelpers.cpp create mode 100644 plugins/oculusLegacy/src/OculusHelpers.h rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculusLegacy/src}/OculusLegacyDisplayPlugin.cpp (90%) rename {libraries/display-plugins/src/display-plugins/oculus => plugins/oculusLegacy/src}/OculusLegacyDisplayPlugin.h (97%) create mode 100644 plugins/oculusLegacy/src/OculusProvider.cpp create mode 100644 plugins/oculusLegacy/src/oculus.json diff --git a/CMakeLists.txt b/CMakeLists.txt index efe99b550b..d1c69e4f6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,6 +203,7 @@ if (NOT ANDROID) add_subdirectory(interface) set_target_properties(interface PROPERTIES FOLDER "Apps") add_subdirectory(tests) + add_subdirectory(plugins) add_subdirectory(tools) endif () diff --git a/cmake/externals/glew/CMakeLists.txt b/cmake/externals/glew/CMakeLists.txt index 5f59cc2377..0a4a0abe71 100644 --- a/cmake/externals/glew/CMakeLists.txt +++ b/cmake/externals/glew/CMakeLists.txt @@ -7,14 +7,15 @@ endif () include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple.zip - URL_MD5 0507dc08337a82a5e7ecbc5417f92cc1 + URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple2.zip + URL_MD5 f05d858e8203c32b689da208ad8b39db CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 ) + # Hide this external target (for ide users) set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") diff --git a/cmake/macros/SetupHifiPlugin.cmake b/cmake/macros/SetupHifiPlugin.cmake new file mode 100644 index 0000000000..0ee94c7816 --- /dev/null +++ b/cmake/macros/SetupHifiPlugin.cmake @@ -0,0 +1,33 @@ +# +# Created by Bradley Austin Davis on 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 +# +macro(SETUP_HIFI_PLUGIN) + set(${TARGET_NAME}_SHARED 1) + setup_hifi_library(${ARGV}) + add_dependencies(interface ${TARGET_NAME}) + set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins") + + if (APPLE) + set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins") + else() + set(PLUGIN_PATH "plugins") + endif() + + # create the destination for the plugin binaries + add_custom_command( + TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory + "${CMAKE_BINARY_DIR}/interface/$/${PLUGIN_PATH}/" + ) + + add_custom_command(TARGET ${DIR} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "$" + "${CMAKE_BINARY_DIR}/interface/$/${PLUGIN_PATH}/" + ) + +endmacro() \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc2eee000d..653f9cf906 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -392,7 +392,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _entityClipboard->createRootElement(); _pluginContainer = new PluginContainerProxy(); - Plugin::setContainer(_pluginContainer); #ifdef Q_OS_WIN installNativeEventFilter(&MyNativeEventFilter::getInstance()); #endif diff --git a/interface/src/PluginContainerProxy.cpp b/interface/src/PluginContainerProxy.cpp index 079d6d0bea..2e5c883897 100644 --- a/interface/src/PluginContainerProxy.cpp +++ b/interface/src/PluginContainerProxy.cpp @@ -13,7 +13,6 @@ #include "ui/DialogsManager.h" PluginContainerProxy::PluginContainerProxy() { - Plugin::setContainer(this); } PluginContainerProxy::~PluginContainerProxy() { diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 14aa03de44..fad244fa5f 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME display-plugins) setup_hifi_library(OpenGL) -link_hifi_libraries(shared plugins gpu gl) +link_hifi_libraries(shared plugins gl) target_opengl() @@ -8,11 +8,6 @@ GroupSources("src/display-plugins") target_oglplus() -add_dependency_external_projects(LibOVR) -find_package(LibOVR REQUIRED) -target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) - if (WIN32) add_dependency_external_projects(OpenVR) find_package(OpenVR REQUIRED) diff --git a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp index 9987ae345c..4b8d957e5f 100644 --- a/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp +++ b/libraries/display-plugins/src/display-plugins/AbstractHMDScriptingInterface.cpp @@ -12,7 +12,6 @@ #include "DisplayPlugin.h" #include -#include static Setting::Handle IPD_SCALE_HANDLE("hmd.ipdScale", 1.0f); @@ -26,12 +25,12 @@ float AbstractHMDScriptingInterface::getIPD() const { float AbstractHMDScriptingInterface::getEyeHeight() const { // FIXME update the display plugin interface to expose per-plugin settings - return OVR_DEFAULT_EYE_HEIGHT; + return getPlayerHeight() - 0.10f; } float AbstractHMDScriptingInterface::getPlayerHeight() const { // FIXME update the display plugin interface to expose per-plugin settings - return OVR_DEFAULT_PLAYER_HEIGHT; + return 1.755f; } float AbstractHMDScriptingInterface::getIPDScale() const { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 914d30d58a..9366ec4403 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -35,36 +35,36 @@ QAction* _vsyncAction{ nullptr }; void Basic2DWindowOpenGLDisplayPlugin::activate() { _framerateActions.clear(); - CONTAINER->addMenuItem(MENU_PATH(), FULLSCREEN, + _container->addMenuItem(MENU_PATH(), FULLSCREEN, [this](bool clicked) { if (clicked) { - CONTAINER->setFullscreen(getFullscreenTarget()); + _container->setFullscreen(getFullscreenTarget()); } else { - CONTAINER->unsetFullscreen(); + _container->unsetFullscreen(); } }, true, false); - CONTAINER->addMenu(FRAMERATE); + _container->addMenu(FRAMERATE); _framerateActions.push_back( - CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_UNLIMITED, + _container->addMenuItem(FRAMERATE, FRAMERATE_UNLIMITED, [this](bool) { updateFramerate(); }, true, true, FRAMERATE)); _framerateActions.push_back( - CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_60, + _container->addMenuItem(FRAMERATE, FRAMERATE_60, [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); _framerateActions.push_back( - CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_50, + _container->addMenuItem(FRAMERATE, FRAMERATE_50, [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); _framerateActions.push_back( - CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_40, + _container->addMenuItem(FRAMERATE, FRAMERATE_40, [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); _framerateActions.push_back( - CONTAINER->addMenuItem(FRAMERATE, FRAMERATE_30, + _container->addMenuItem(FRAMERATE, FRAMERATE_30, [this](bool) { updateFramerate(); }, true, false, FRAMERATE)); WindowOpenGLDisplayPlugin::activate(); // Vsync detection happens in the parent class activate, so we need to check after that if (_vsyncSupported) { - _vsyncAction = CONTAINER->addMenuItem(MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true); + _vsyncAction = _container->addMenuItem(MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true); } else { _vsyncAction = nullptr; } @@ -107,7 +107,7 @@ int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const { bool Basic2DWindowOpenGLDisplayPlugin::isThrottled() const { static const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Menu.h - bool shouldThrottle = (!CONTAINER->isForeground() && CONTAINER->isOptionChecked(ThrottleFPSIfNotFocus)); + bool shouldThrottle = (!_container->isForeground() && _container->isOptionChecked(ThrottleFPSIfNotFocus)); if (_isThrottled != shouldThrottle) { _isThrottled = shouldThrottle; diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp index 4af45d299b..8155d69826 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.cpp @@ -15,9 +15,6 @@ #include "Basic2DWindowOpenGLDisplayPlugin.h" #include "openvr/OpenVrDisplayPlugin.h" -#include "oculus/OculusDisplayPlugin.h" -#include "oculus/OculusDebugDisplayPlugin.h" -#include "oculus/OculusLegacyDisplayPlugin.h" const QString& DisplayPlugin::MENU_PATH() { static const QString value = "Display"; @@ -40,15 +37,6 @@ DisplayPluginList getDisplayPlugins() { new InterleavedStereoDisplayPlugin(), // HMDs - - // Windows Oculus SDK - new OculusDisplayPlugin(), - // Windows Oculus Simulator... uses head tracking and the same rendering - // as the connected hardware, but without using the SDK to display to the - // Rift. Useful for debugging Rift performance with nSight. - new OculusDebugDisplayPlugin(), - // Mac/Linux Oculus SDK (0.5) - new OculusLegacyDisplayPlugin(), #ifdef Q_OS_WIN // SteamVR SDK new OpenVrDisplayPlugin(), diff --git a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h index b2176e0bd1..84c6592c53 100644 --- a/libraries/display-plugins/src/display-plugins/DisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/DisplayPlugin.h @@ -5,135 +5,6 @@ // 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 -#include "plugins/Plugin.h" -#include -#include -#include - -#include -#include - -#include -#include -#include - -enum Eye { - Left, - Right, - Mono -}; - -/* - * Helper method to iterate over each eye - */ -template -void for_each_eye(F f) { - f(Left); - f(Right); -} - -/* - * Helper method to iterate over each eye, with an additional lambda to take action between the eyes - */ -template -void for_each_eye(F f, FF ff) { - f(Eye::Left); - ff(); - f(Eye::Right); -} - -class QWindow; - -#define AVERAGE_HUMAN_IPD 0.064f - -class DisplayPlugin : public Plugin { - Q_OBJECT -public: - virtual bool isHmd() const { return false; } - virtual int getHmdScreen() const { return -1; } - /// By default, all HMDs are stereo - virtual bool isStereo() const { return isHmd(); } - virtual bool isThrottled() const { return false; } - - // Rendering support - - // Stop requesting renders, but don't do full deactivation - // needed to work around the issues caused by Oculus - // processing messages in the middle of submitFrame - virtual void stop() = 0; - - /** - * Called by the application before the frame rendering. Can be used for - * render timing related calls (for instance, the Oculus begin frame timing - * call) - */ - virtual void preRender() = 0; - /** - * Called by the application immediately before calling the display function. - * For OpenGL based plugins, this is the best place to put activate the output - * OpenGL context - */ - virtual void preDisplay() = 0; - - /** - * Sends the scene texture to the display plugin. - */ - virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) = 0; - - /** - * Called by the application immeidately after display. For OpenGL based - * displays, this is the best place to put the buffer swap - */ - virtual void finishFrame() = 0; - - // Does the rendering surface have current focus? - virtual bool hasFocus() const = 0; - - // The size of the rendering target (may be larger than the device size due to distortion) - virtual glm::uvec2 getRecommendedRenderSize() const = 0; - - // The size of the UI - virtual glm::uvec2 getRecommendedUiSize() const { - return getRecommendedRenderSize(); - } - - // By default the aspect ratio is just the render size - virtual float getRecommendedAspectRatio() const { - return aspect(getRecommendedRenderSize()); - } - - // Stereo specific methods - virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const { - return baseProjection; - } - - // HMD specific methods - // TODO move these into another class? - virtual glm::mat4 getEyeToHeadTransform(Eye eye) const { - static const glm::mat4 transform; return transform; - } - - virtual glm::mat4 getHeadPose() const { - static const glm::mat4 pose; return pose; - } - - // Needed for timewarp style features - virtual void setEyeRenderPose(Eye eye, const glm::mat4& pose) { - // NOOP - } - - virtual float getIPD() const { return AVERAGE_HUMAN_IPD; } - - virtual void abandonCalibration() {} - virtual void resetSensors() {} - virtual float devicePixelRatio() { return 1.0; } - - - static const QString& MENU_PATH(); -signals: - void recommendedFramebufferSizeChanged(const QSize & size); - void requestRender(); -}; +#include diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 914f80d983..ce512962ff 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -25,6 +25,6 @@ bool NullDisplayPlugin::hasFocus() const { void NullDisplayPlugin::preRender() {} void NullDisplayPlugin::preDisplay() {} -void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} +void NullDisplayPlugin::display(uint32_t sceneTexture, const glm::uvec2& sceneSize) {} void NullDisplayPlugin::finishFrame() {} void NullDisplayPlugin::stop() {} diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 4f2cc77b8f..8cd5c2bc37 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -21,7 +21,7 @@ public: virtual bool hasFocus() const override; virtual void preRender() override; virtual void preDisplay() override; - virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override; + virtual void display(uint32_t sceneTexture, const glm::uvec2& sceneSize) override; virtual void finishFrame() override; private: diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 3791375c9e..3ef882fe76 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp index ffea6605af..6ddc791503 100644 --- a/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/WindowOpenGLDisplayPlugin.cpp @@ -42,7 +42,7 @@ bool WindowOpenGLDisplayPlugin::hasFocus() const { void WindowOpenGLDisplayPlugin::activate() { OpenGLDisplayPlugin::activate(); - _window = CONTAINER->getPrimarySurface(); + _window = _container->getPrimarySurface(); _window->makeCurrent(); customizeContext(); _window->doneCurrent(); diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 245fd11ef7..174bf1bf36 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -89,7 +89,7 @@ bool OpenVrDisplayPlugin::isSupported() const { } void OpenVrDisplayPlugin::activate() { - CONTAINER->setIsOptionChecked(StandingHMDSensorMode, true); + _container->setIsOptionChecked(StandingHMDSensorMode, true); hmdRefCount++; vr::HmdError eError = vr::HmdError_None; @@ -132,7 +132,7 @@ void OpenVrDisplayPlugin::activate() { } void OpenVrDisplayPlugin::deactivate() { - CONTAINER->setIsOptionChecked(StandingHMDSensorMode, false); + _container->setIsOptionChecked(StandingHMDSensorMode, false); hmdRefCount--; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index 4f7b0a1a78..f7e71313df 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -74,21 +74,21 @@ void StereoDisplayPlugin::activate() { if (screen == qApp->primaryScreen()) { checked = true; } - auto action = CONTAINER->addMenuItem(MENU_PATH(), name, + auto action = _container->addMenuItem(MENU_PATH(), name, [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); _screenActions[i] = action; } - CONTAINER->removeMenu(FRAMERATE); + _container->removeMenu(FRAMERATE); - CONTAINER->setFullscreen(qApp->primaryScreen()); + _container->setFullscreen(qApp->primaryScreen()); WindowOpenGLDisplayPlugin::activate(); } void StereoDisplayPlugin::updateScreen() { for (uint32_t i = 0; i < _screenActions.size(); ++i) { if (_screenActions[i]->isChecked()) { - CONTAINER->setFullscreen(qApp->screens().at(i)); + _container->setFullscreen(qApp->screens().at(i)); break; } } @@ -96,7 +96,7 @@ void StereoDisplayPlugin::updateScreen() { void StereoDisplayPlugin::deactivate() { _screenActions.clear(); - CONTAINER->unsetFullscreen(); + _container->unsetFullscreen(); WindowOpenGLDisplayPlugin::deactivate(); } diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.h b/libraries/input-plugins/src/input-plugins/InputPlugin.h index 787922e04c..d03f884ec7 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.h +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.h @@ -10,14 +10,4 @@ // #pragma once -#include - -class InputPlugin : public Plugin { -public: - virtual bool isJointController() const = 0; - - virtual void pluginFocusOutEvent() = 0; - - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) = 0; -}; - +#include diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 5e0f3277b1..024eb86182 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -94,8 +94,8 @@ void SixenseManager::activate() { _calibrationState = CALIBRATION_STATE_IDLE; _avatarPosition = DEFAULT_AVATAR_POSITION; - CONTAINER->addMenu(MENU_PATH); - CONTAINER->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, + _container->addMenu(MENU_PATH); + _container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, [this] (bool clicked) { this->setSixenseFilter(clicked); }, true, true); @@ -136,8 +136,8 @@ void SixenseManager::deactivate() { InputPlugin::deactivate(); #ifdef HAVE_SIXENSE - CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); - CONTAINER->removeMenu(MENU_PATH); + _container->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); + _container->removeMenu(MENU_PATH); _poseStateMap.clear(); _collectedSamples.clear(); @@ -319,7 +319,7 @@ void SixenseManager::updateCalibration(void* controllersX) { _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; - CONTAINER->requestReset(); + _container->requestReset(); qCDebug(inputplugins, "succeess: sixense calibration"); } break; diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index e90006e014..69b2b5b2c6 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -75,8 +75,8 @@ bool ViveControllerManager::isSupported() const { void ViveControllerManager::activate() { InputPlugin::activate(); #ifdef Q_OS_WIN - CONTAINER->addMenu(MENU_PATH); - CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, + _container->addMenu(MENU_PATH); + _container->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, [this] (bool clicked) { this->setRenderControllers(clicked); }, true, true); @@ -146,8 +146,8 @@ void ViveControllerManager::deactivate() { InputPlugin::deactivate(); #ifdef Q_OS_WIN - CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); - CONTAINER->removeMenu(MENU_PATH); + _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); + _container->removeMenu(MENU_PATH); hmdRefCount--; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h new file mode 100644 index 0000000000..5b00391f09 --- /dev/null +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -0,0 +1,140 @@ +// +// Created by Bradley Austin Davis on 2015/05/29 +// 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 + +#include + +#include +#include + +#include +#include + +#include +#include + +#include "Plugin.h" + +enum Eye { + Left, + Right, + Mono +}; + +/* + * Helper method to iterate over each eye + */ +template +void for_each_eye(F f) { + f(Left); + f(Right); +} + +/* + * Helper method to iterate over each eye, with an additional lambda to take action between the eyes + */ +template +void for_each_eye(F f, FF ff) { + f(Eye::Left); + ff(); + f(Eye::Right); +} + +class QWindow; + +#define AVERAGE_HUMAN_IPD 0.064f + +class DisplayPlugin : public Plugin { + Q_OBJECT +public: + virtual bool isHmd() const { return false; } + virtual int getHmdScreen() const { return -1; } + /// By default, all HMDs are stereo + virtual bool isStereo() const { return isHmd(); } + virtual bool isThrottled() const { return false; } + + // Rendering support + + // Stop requesting renders, but don't do full deactivation + // needed to work around the issues caused by Oculus + // processing messages in the middle of submitFrame + virtual void stop() = 0; + + /** + * Called by the application before the frame rendering. Can be used for + * render timing related calls (for instance, the Oculus begin frame timing + * call) + */ + virtual void preRender() = 0; + /** + * Called by the application immediately before calling the display function. + * For OpenGL based plugins, this is the best place to put activate the output + * OpenGL context + */ + virtual void preDisplay() = 0; + + /** + * Sends the scene texture to the display plugin. + */ + virtual void display(uint32_t sceneTexture, const glm::uvec2& sceneSize) = 0; + + /** + * Called by the application immeidately after display. For OpenGL based + * displays, this is the best place to put the buffer swap + */ + virtual void finishFrame() = 0; + + // Does the rendering surface have current focus? + virtual bool hasFocus() const = 0; + + // The size of the rendering target (may be larger than the device size due to distortion) + virtual glm::uvec2 getRecommendedRenderSize() const = 0; + + // The size of the UI + virtual glm::uvec2 getRecommendedUiSize() const { + return getRecommendedRenderSize(); + } + + // By default the aspect ratio is just the render size + virtual float getRecommendedAspectRatio() const { + return aspect(getRecommendedRenderSize()); + } + + // Stereo specific methods + virtual glm::mat4 getProjection(Eye eye, const glm::mat4& baseProjection) const { + return baseProjection; + } + + // HMD specific methods + // TODO move these into another class? + virtual glm::mat4 getEyeToHeadTransform(Eye eye) const { + static const glm::mat4 transform; return transform; + } + + virtual glm::mat4 getHeadPose() const { + static const glm::mat4 pose; return pose; + } + + // Needed for timewarp style features + virtual void setEyeRenderPose(Eye eye, const glm::mat4& pose) { + // NOOP + } + + virtual float getIPD() const { return AVERAGE_HUMAN_IPD; } + + virtual void abandonCalibration() {} + virtual void resetSensors() {} + virtual float devicePixelRatio() { return 1.0; } + + + static const QString& MENU_PATH(); +signals: + void recommendedFramebufferSizeChanged(const QSize & size); + void requestRender(); +}; + diff --git a/libraries/plugins/src/plugins/Forward.h b/libraries/plugins/src/plugins/Forward.h index 1a9298d13c..78ec8fdcb3 100644 --- a/libraries/plugins/src/plugins/Forward.h +++ b/libraries/plugins/src/plugins/Forward.h @@ -21,3 +21,4 @@ using DisplayPluginPointer = QSharedPointer; using DisplayPluginList = QVector; using InputPluginPointer = QSharedPointer; using InputPluginList = QVector; + diff --git a/libraries/plugins/src/plugins/InputPlugin.h b/libraries/plugins/src/plugins/InputPlugin.h new file mode 100644 index 0000000000..e9d8ac8d86 --- /dev/null +++ b/libraries/plugins/src/plugins/InputPlugin.h @@ -0,0 +1,23 @@ +// +// InputPlugin.h +// input-plugins/src/input-plugins +// +// Created by Sam Gondelman on 7/13/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 +// +#pragma once + +#include "Plugin.h" + +class InputPlugin : public Plugin { +public: + virtual bool isJointController() const = 0; + + virtual void pluginFocusOutEvent() = 0; + + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) = 0; +}; + diff --git a/libraries/plugins/src/plugins/Plugin.cpp b/libraries/plugins/src/plugins/Plugin.cpp index 2c0b9fa5cf..7c30f252c9 100644 --- a/libraries/plugins/src/plugins/Plugin.cpp +++ b/libraries/plugins/src/plugins/Plugin.cpp @@ -7,12 +7,10 @@ // #include "Plugin.h" -PluginContainer* Plugin::CONTAINER{ nullptr }; - QString Plugin::UNKNOWN_PLUGIN_ID("unknown"); void Plugin::setContainer(PluginContainer* container) { - CONTAINER = container; + _container = container; } bool Plugin::isSupported() const { return true; } diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h index f53d309e97..c030b1073f 100644 --- a/libraries/plugins/src/plugins/Plugin.h +++ b/libraries/plugins/src/plugins/Plugin.h @@ -24,7 +24,7 @@ public: virtual bool isSupported() const; - static void setContainer(PluginContainer* container); + void setContainer(PluginContainer* container); /// Called when plugin is initially loaded, typically at application start virtual void init(); @@ -57,8 +57,8 @@ public: virtual void loadSettings() {} protected: - bool _active{ false }; - static PluginContainer* CONTAINER; + bool _active { false }; + PluginContainer* _container { nullptr }; static QString UNKNOWN_PLUGIN_ID; }; diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index 2deb41fb13..27e326fcba 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -6,15 +6,54 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "PluginManager.h" + #include -#include "Forward.h" +#include +#include +#include +#include + +#include "RuntimePlugin.h" +#include "DisplayPlugin.h" +#include "InputPlugin.h" +#include "PluginContainer.h" + PluginManager* PluginManager::getInstance() { static PluginManager _manager; return &_manager; } +using Loader = QSharedPointer; +using LoaderList = QList; + +const LoaderList& getLoadedPlugins() { + static std::once_flag once; + static LoaderList loadedPlugins; + std::call_once(once, [&] { + QString pluginPath = QCoreApplication::applicationDirPath() + "/plugins/"; + QDir pluginDir(pluginPath); + pluginDir.setFilter(QDir::Files); + if (pluginDir.exists()) { + qDebug() << "Loading runtime plugins from " << pluginPath; + auto candidates = pluginDir.entryList(); + for (auto plugin : candidates) { + qDebug() << "Attempting plugins " << plugin; + QSharedPointer loader(new QPluginLoader(pluginPath + plugin)); + if (loader->load()) { + qDebug() << "Plugins " << plugin << " success"; + loadedPlugins.push_back(loader); + } + } + } + }); + return loadedPlugins; +} + +PluginManager::PluginManager() { +} + // TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class extern DisplayPluginList getDisplayPlugins(); extern InputPluginList getInputPlugins(); @@ -23,8 +62,25 @@ extern void saveInputPluginSettings(const InputPluginList& plugins); const DisplayPluginList& PluginManager::getDisplayPlugins() { static DisplayPluginList displayPlugins; static std::once_flag once; + std::call_once(once, [&] { + // Grab the built in plugins displayPlugins = ::getDisplayPlugins(); + + // Now grab the dynamic plugins + for (auto loader : getLoadedPlugins()) { + DisplayProvider* displayProvider = qobject_cast(loader->instance()); + if (displayProvider) { + for (auto displayPlugin : displayProvider->getDisplayPlugins()) { + displayPlugins.push_back(displayPlugin); + } + } + } + auto& container = PluginContainer::getInstance(); + for (auto plugin : displayPlugins) { + plugin->setContainer(&container); + } + }); return displayPlugins; } @@ -34,6 +90,21 @@ const InputPluginList& PluginManager::getInputPlugins() { static std::once_flag once; std::call_once(once, [&] { inputPlugins = ::getInputPlugins(); + + // Now grab the dynamic plugins + for (auto loader : getLoadedPlugins()) { + InputProvider* inputProvider = qobject_cast(loader->instance()); + if (inputProvider) { + for (auto inputPlugin : inputProvider->getInputPlugins()) { + inputPlugins.push_back(inputPlugin); + } + } + } + + auto& container = PluginContainer::getInstance(); + for (auto plugin : inputPlugins) { + plugin->setContainer(&container); + } }); return inputPlugins; } diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 09dcc9d107..17619a93c0 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -12,6 +12,7 @@ class PluginManager : public QObject { public: static PluginManager* getInstance(); + PluginManager(); const DisplayPluginList& getDisplayPlugins(); const InputPluginList& getInputPlugins(); diff --git a/libraries/plugins/src/plugins/RuntimePlugin.h b/libraries/plugins/src/plugins/RuntimePlugin.h new file mode 100644 index 0000000000..d7bf31ea28 --- /dev/null +++ b/libraries/plugins/src/plugins/RuntimePlugin.h @@ -0,0 +1,36 @@ +// +// Created by Bradley Austin Davis on 2015/10/24 +// 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 + +#include + +#include +#include + +#include "Forward.h" + +class DisplayProvider { +public: + virtual ~DisplayProvider() {} + + virtual DisplayPluginList getDisplayPlugins() = 0; +}; + +#define DisplayProvider_iid "com.highfidelity.plugins.display" +Q_DECLARE_INTERFACE(DisplayProvider, DisplayProvider_iid) + + +class InputProvider { +public: + virtual ~InputProvider() {} + virtual InputPluginList getInputPlugins() = 0; +}; + +#define InputProvider_iid "com.highfidelity.plugins.input" +Q_DECLARE_INTERFACE(InputProvider, InputProvider_iid) + diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 0000000000..55b18b122c --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,18 @@ +# +# Created by Bradley Austin Davis on 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 +# + +# add the plugin directories +file(GLOB PLUGIN_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*") +list(REMOVE_ITEM PLUGIN_SUBDIRS "CMakeFiles") + +foreach(DIR ${PLUGIN_SUBDIRS}) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") + add_subdirectory(${DIR}) + endif() +endforeach() + diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt new file mode 100644 index 0000000000..62999cbb7e --- /dev/null +++ b/plugins/oculus/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Created by Bradley Austin Davis on 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 +# + +if (WIN32) + + set(TARGET_NAME oculus) + setup_hifi_plugin() + link_hifi_libraries(shared gl plugins display-plugins) + + include_hifi_library_headers(octree) + + add_dependency_external_projects(LibOVR) + find_package(LibOVR REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) + +endif() \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp similarity index 95% rename from libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp rename to plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 859a4a810a..4c80b9a51d 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -61,6 +61,14 @@ bool OculusBaseDisplayPlugin::isSupported() const { #endif } +// DLL based display plugins MUST initialize GLEW inside the DLL code. +void OculusBaseDisplayPlugin::customizeContext() { + glewExperimental = true; + GLenum err = glewInit(); + glGetError(); + WindowOpenGLDisplayPlugin::customizeContext(); +} + void OculusBaseDisplayPlugin::init() { } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h similarity index 96% rename from libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h rename to plugins/oculus/src/OculusBaseDisplayPlugin.h index 6307f6bbf9..ba1924bfff 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -7,7 +7,7 @@ // #pragma once -#include "../WindowOpenGLDisplayPlugin.h" +#include #include @@ -35,6 +35,7 @@ public: virtual float getIPD() const override final; protected: + virtual void customizeContext() override; virtual void preRender() override final; virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.cpp b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp similarity index 100% rename from libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.cpp rename to plugins/oculus/src/OculusDebugDisplayPlugin.cpp diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h similarity index 100% rename from libraries/display-plugins/src/display-plugins/oculus/OculusDebugDisplayPlugin.h rename to plugins/oculus/src/OculusDebugDisplayPlugin.h diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp similarity index 97% rename from libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp rename to plugins/oculus/src/OculusDisplayPlugin.cpp index 3e2290f104..923b8bde6e 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -7,11 +7,14 @@ // #include "OculusDisplayPlugin.h" -#include +#include + +// FIXME get rid of this +#include +#include #include "OculusHelpers.h" -#include #if (OVR_MAJOR_VERSION >= 6) @@ -142,16 +145,16 @@ static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; void OculusDisplayPlugin::activate() { - CONTAINER->addMenuItem(MENU_PATH(), MONO_PREVIEW, + _container->addMenuItem(MENU_PATH(), MONO_PREVIEW, [this](bool clicked) { _monoPreview = clicked; }, true, true); - CONTAINER->removeMenu(FRAMERATE); + _container->removeMenu(FRAMERATE); OculusBaseDisplayPlugin::activate(); } void OculusDisplayPlugin::customizeContext() { - WindowOpenGLDisplayPlugin::customizeContext(); + OculusBaseDisplayPlugin::customizeContext(); #if (OVR_MAJOR_VERSION >= 6) _sceneFbo = SwapFboPtr(new SwapFramebufferWrapper(_hmd)); _sceneFbo->Init(getRecommendedRenderSize()); diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h similarity index 100% rename from libraries/display-plugins/src/display-plugins/oculus/OculusDisplayPlugin.h rename to plugins/oculus/src/OculusDisplayPlugin.h diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.cpp b/plugins/oculus/src/OculusHelpers.cpp similarity index 100% rename from libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.cpp rename to plugins/oculus/src/OculusHelpers.cpp diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h b/plugins/oculus/src/OculusHelpers.h similarity index 100% rename from libraries/display-plugins/src/display-plugins/oculus/OculusHelpers.h rename to plugins/oculus/src/OculusHelpers.h diff --git a/plugins/oculus/src/OculusProvider.cpp b/plugins/oculus/src/OculusProvider.cpp new file mode 100644 index 0000000000..40dfb9df9a --- /dev/null +++ b/plugins/oculus/src/OculusProvider.cpp @@ -0,0 +1,54 @@ +// +// Created by Bradley Austin Davis on 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 + +#include +#include +#include + +#include +#include + +#include "OculusDisplayPlugin.h" +#include "OculusDebugDisplayPlugin.h" + +class OculusProvider : public QObject, public DisplayProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID DisplayProvider_iid FILE "oculus.json") + Q_INTERFACES(DisplayProvider) + +public: + OculusProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~OculusProvider() {} + + virtual DisplayPluginList getDisplayPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + DisplayPluginPointer plugin(new OculusDisplayPlugin()); + if (plugin->isSupported()) { + _displayPlugins.push_back(plugin); + } + + // Windows Oculus Simulator... uses head tracking and the same rendering + // as the connected hardware, but without using the SDK to display to the + // Rift. Useful for debugging Rift performance with nSight. + plugin = DisplayPluginPointer(new OculusDebugDisplayPlugin()); + if (plugin->isSupported()) { + _displayPlugins.push_back(plugin); + } + }); + return _displayPlugins; + } + +private: + DisplayPluginList _displayPlugins; +}; + +#include "OculusProvider.moc" diff --git a/plugins/oculus/src/oculus.json b/plugins/oculus/src/oculus.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/oculus/src/oculus.json @@ -0,0 +1 @@ +{} diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt new file mode 100644 index 0000000000..bf9d22410d --- /dev/null +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Created by Bradley Austin Davis on 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 +# + +if (NOT WIN32) + + set(TARGET_NAME oculusLegacy) + setup_hifi_plugin() + link_hifi_libraries(shared gl plugins display-plugins) + + include_hifi_library_headers(octree) + + add_dependency_external_projects(LibOVR) + find_package(LibOVR REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${LIBOVR_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) + +endif() \ No newline at end of file diff --git a/plugins/oculusLegacy/src/OculusHelpers.cpp b/plugins/oculusLegacy/src/OculusHelpers.cpp new file mode 100644 index 0000000000..fff2a38344 --- /dev/null +++ b/plugins/oculusLegacy/src/OculusHelpers.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis on 2015/08/08 +// 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 "OculusHelpers.h" diff --git a/plugins/oculusLegacy/src/OculusHelpers.h b/plugins/oculusLegacy/src/OculusHelpers.h new file mode 100644 index 0000000000..b4bcdc1511 --- /dev/null +++ b/plugins/oculusLegacy/src/OculusHelpers.h @@ -0,0 +1,85 @@ +// +// Created by Bradley Austin Davis on 2015/05/26 +// 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 + +#include +#include +#include +#include + +// Convenience method for looping over each eye with a lambda +template +inline void ovr_for_each_eye(Function function) { + for (ovrEyeType eye = ovrEyeType::ovrEye_Left; + eye < ovrEyeType::ovrEye_Count; + eye = static_cast(eye + 1)) { + function(eye); + } +} + +inline glm::mat4 toGlm(const ovrMatrix4f & om) { + return glm::transpose(glm::make_mat4(&om.M[0][0])); +} + +inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) { + return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true)); +} + +inline glm::vec3 toGlm(const ovrVector3f & ov) { + return glm::make_vec3(&ov.x); +} + +inline glm::vec2 toGlm(const ovrVector2f & ov) { + return glm::make_vec2(&ov.x); +} + +inline glm::uvec2 toGlm(const ovrSizei & ov) { + return glm::uvec2(ov.w, ov.h); +} + +inline glm::quat toGlm(const ovrQuatf & oq) { + return glm::make_quat(&oq.x); +} + +inline glm::mat4 toGlm(const ovrPosef & op) { + glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation)); + glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position)); + return translation * orientation; +} + +inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) { + ovrMatrix4f result; + glm::mat4 transposed(glm::transpose(m)); + memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16); + return result; +} + +inline ovrVector3f ovrFromGlm(const glm::vec3 & v) { + return{ v.x, v.y, v.z }; +} + +inline ovrVector2f ovrFromGlm(const glm::vec2 & v) { + return{ v.x, v.y }; +} + +inline ovrSizei ovrFromGlm(const glm::uvec2 & v) { + return{ (int)v.x, (int)v.y }; +} + +inline ovrQuatf ovrFromGlm(const glm::quat & q) { + return{ q.x, q.y, q.z, q.w }; +} + +inline ovrPosef ovrPoseFromGlm(const glm::mat4 & m) { + glm::vec3 translation = glm::vec3(m[3]) / m[3].w; + glm::quat orientation = glm::quat_cast(m); + ovrPosef result; + result.Orientation = ovrFromGlm(orientation); + result.Position = ovrFromGlm(translation); + return result; +} diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp similarity index 90% rename from libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp rename to plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 205444397f..5a253cdbbf 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -42,10 +42,8 @@ uvec2 OculusLegacyDisplayPlugin::getRecommendedRenderSize() const { } void OculusLegacyDisplayPlugin::preRender() { -#if (OVR_MAJOR_VERSION == 5) ovrHmd_GetEyePoses(_hmd, _frameIndex, _eyeOffsets, _eyePoses, &_trackingState); ovrHmd_BeginFrame(_hmd, _frameIndex); -#endif WindowOpenGLDisplayPlugin::preRender(); } @@ -54,32 +52,21 @@ glm::mat4 OculusLegacyDisplayPlugin::getProjection(Eye eye, const glm::mat4& bas } void OculusLegacyDisplayPlugin::resetSensors() { -#if (OVR_MAJOR_VERSION == 5) ovrHmd_RecenterPose(_hmd); -#endif } glm::mat4 OculusLegacyDisplayPlugin::getEyeToHeadTransform(Eye eye) const { -#if (OVR_MAJOR_VERSION == 5) return toGlm(_eyePoses[eye]); -#else - return WindowOpenGLDisplayPlugin::getEyeToHeadTransform(eye); -#endif } // Should NOT be used for rendering as this will mess up timewarp. Use the getModelview() method above for // any use of head poses for rendering, ensuring you use the correct eye glm::mat4 OculusLegacyDisplayPlugin::getHeadPose() const { -#if (OVR_MAJOR_VERSION == 5) return toGlm(_trackingState.HeadPose.ThePose); -#else - return WindowOpenGLDisplayPlugin::getHeadPose(); -#endif } bool OculusLegacyDisplayPlugin::isSupported() const { -#if (OVR_MAJOR_VERSION == 5) if (!ovr_Initialize(nullptr)) { return false; } @@ -104,14 +91,10 @@ bool OculusLegacyDisplayPlugin::isSupported() const { ovr_Shutdown(); return result; -#else - return false; -#endif } void OculusLegacyDisplayPlugin::activate() { -#if (OVR_MAJOR_VERSION == 5) - if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { + if (!(ovr_Initialize(nullptr))) { Q_ASSERT(false); qFatal("Failed to Initialize SDK"); } @@ -149,8 +132,8 @@ void OculusLegacyDisplayPlugin::activate() { _frameIndex = 0; - if (!OVR_SUCCESS(ovrHmd_ConfigureTracking(_hmd, - ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0))) { + if (!ovrHmd_ConfigureTracking(_hmd, + ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection, 0)) { qFatal("Could not attach to sensor device"); } @@ -158,7 +141,7 @@ void OculusLegacyDisplayPlugin::activate() { int screen = getHmdScreen(); if (screen != -1) { - CONTAINER->setFullscreen(qApp->screens()[screen]); + _container->setFullscreen(qApp->screens()[screen]); } _window->installEventFilter(this); @@ -189,11 +172,9 @@ void OculusLegacyDisplayPlugin::activate() { #endif ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); Q_ASSERT(result); -#endif } void OculusLegacyDisplayPlugin::deactivate() { -#if (OVR_MAJOR_VERSION == 5) _window->removeEventFilter(this); WindowOpenGLDisplayPlugin::deactivate(); @@ -202,12 +183,11 @@ void OculusLegacyDisplayPlugin::deactivate() { if (_hmdScreen >= 0) { riftScreen = qApp->screens()[_hmdScreen]; } - CONTAINER->unsetFullscreen(riftScreen); + _container->unsetFullscreen(riftScreen); ovrHmd_Destroy(_hmd); _hmd = nullptr; ovr_Shutdown(); -#endif } void OculusLegacyDisplayPlugin::preDisplay() { @@ -216,17 +196,14 @@ void OculusLegacyDisplayPlugin::preDisplay() { void OculusLegacyDisplayPlugin::display(GLuint finalTexture, const glm::uvec2& sceneSize) { ++_frameIndex; -#if (OVR_MAJOR_VERSION == 5) ovr_for_each_eye([&](ovrEyeType eye) { reinterpret_cast(_eyeTextures[eye]).OGL.TexId = finalTexture; }); ovrHmd_EndFrame(_hmd, _eyePoses, _eyeTextures); -#endif } // Pass input events on to the application bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { -#if (OVR_MAJOR_VERSION == 5) if (!_hswDismissed && (event->type() == QEvent::KeyPress)) { static ovrHSWDisplayState hswState; ovrHmd_GetHSWDisplayState(_hmd, &hswState); @@ -236,7 +213,6 @@ bool OculusLegacyDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { _hswDismissed = true; } } -#endif return WindowOpenGLDisplayPlugin::eventFilter(receiver, event); } diff --git a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h similarity index 97% rename from libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h rename to plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 9e2e47f434..2e95cee9bb 100644 --- a/libraries/display-plugins/src/display-plugins/oculus/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -7,7 +7,7 @@ // #pragma once -#include "../WindowOpenGLDisplayPlugin.h" +#include #include diff --git a/plugins/oculusLegacy/src/OculusProvider.cpp b/plugins/oculusLegacy/src/OculusProvider.cpp new file mode 100644 index 0000000000..606563e0ad --- /dev/null +++ b/plugins/oculusLegacy/src/OculusProvider.cpp @@ -0,0 +1,45 @@ +// +// Created by Bradley Austin Davis on 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 + +#include +#include +#include + +#include +#include + +#include "OculusLegacyDisplayPlugin.h" + +class OculusProvider : public QObject, public DisplayProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID DisplayProvider_iid FILE "oculus.json") + Q_INTERFACES(DisplayProvider) + +public: + OculusProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~OculusProvider() {} + + virtual DisplayPluginList getDisplayPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + DisplayPluginPointer plugin(new OculusLegacyDisplayPlugin()); + if (plugin->isSupported()) { + _displayPlugins.push_back(plugin); + } + }); + return _displayPlugins; + } + +private: + DisplayPluginList _displayPlugins; +}; + +#include "OculusProvider.moc" diff --git a/plugins/oculusLegacy/src/oculus.json b/plugins/oculusLegacy/src/oculus.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/oculusLegacy/src/oculus.json @@ -0,0 +1 @@ +{} diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index a7b1be15ca..139d9b282c 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -80,9 +80,6 @@ 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 {} From 43df5b55fe5adf6c3fefdc61c431225fe7587668 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 1 Nov 2015 11:55:55 -0800 Subject: [PATCH 0600/1003] fix linker warnings --- .../controllers/src/controllers/DeviceProxy.cpp | 11 +++++++---- .../controllers/src/controllers/impl/Endpoint.cpp | 12 +++++++----- .../controllers/src/controllers/impl/Mapping.cpp | 9 ++++++++- libraries/controllers/src/controllers/impl/Route.cpp | 8 +++++++- .../impl/conditionals/EndpointConditional.cpp | 8 +++++++- .../controllers/impl/conditionals/NotConditional.cpp | 8 +++++++- .../src/controllers/impl/endpoints/ArrayEndpoint.cpp | 8 +++++++- .../src/controllers/impl/endpoints/JSEndpoint.cpp | 8 +++++++- .../controllers/impl/endpoints/StandardEndpoint.cpp | 8 +++++++- .../impl/filters/ConstrainToIntegerFilter.cpp | 8 +++++++- .../filters/ConstrainToPositiveIntegerFilter.cpp | 8 +++++++- .../src/controllers/impl/filters/InvertFilter.cpp | 8 +++++++- 12 files changed, 85 insertions(+), 19 deletions(-) diff --git a/libraries/controllers/src/controllers/DeviceProxy.cpp b/libraries/controllers/src/controllers/DeviceProxy.cpp index f3e9526080..f03354c52d 100644 --- a/libraries/controllers/src/controllers/DeviceProxy.cpp +++ b/libraries/controllers/src/controllers/DeviceProxy.cpp @@ -7,8 +7,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "DeviceProxy.h" - -namespace controller { -} +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "DeviceProxy.h" diff --git a/libraries/controllers/src/controllers/impl/Endpoint.cpp b/libraries/controllers/src/controllers/impl/Endpoint.cpp index 9e9b13f8ea..e771b1916f 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.cpp +++ b/libraries/controllers/src/controllers/impl/Endpoint.cpp @@ -6,9 +6,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Endpoint.h" - -namespace controller { - -} +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "Endpoint.h" diff --git a/libraries/controllers/src/controllers/impl/Mapping.cpp b/libraries/controllers/src/controllers/impl/Mapping.cpp index 68c43da393..dd8e1c1d48 100644 --- a/libraries/controllers/src/controllers/impl/Mapping.cpp +++ b/libraries/controllers/src/controllers/impl/Mapping.cpp @@ -5,4 +5,11 @@ // 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" + +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "Mapping.h" diff --git a/libraries/controllers/src/controllers/impl/Route.cpp b/libraries/controllers/src/controllers/impl/Route.cpp index 56590e564a..c74f809195 100644 --- a/libraries/controllers/src/controllers/impl/Route.cpp +++ b/libraries/controllers/src/controllers/impl/Route.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Route.h" +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "Route.h" diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp index 03e16b8cf9..f833eedb60 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "EndpointConditional.h" \ No newline at end of file +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "EndpointConditional.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp index 0c8d602b9e..e30b060985 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "NotConditional.h" +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "NotConditional.h" diff --git a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp index c083a7147d..5dea1de34e 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ArrayEndpoint.h" \ No newline at end of file +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "ArrayEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp index 4560741d12..7f0e80cbae 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "JSEndpoint.h" \ No newline at end of file +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "JSEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp index 09920d249c..89bbe5d777 100644 --- a/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp +++ b/libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "StandardEndpoint.h" \ No newline at end of file +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "StandardEndpoint.h" \ No newline at end of file diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp index 78ffb47693..8bd3d2db89 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ConstrainToIntegerFilter.h" +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "ConstrainToIntegerFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp index d78942b18f..f1abc8cecd 100644 --- a/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ConstrainToPositiveIntegerFilter.h" +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "ConstrainToPositiveIntegerFilter.h" diff --git a/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp index db582b84cc..5407c6dd1d 100644 --- a/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp @@ -6,4 +6,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "InvertFilter.h" +// NOTE: we don't need to include this header unless/until we add additional symbols. +// By removing this header we prevent these warnings on windows: +// +// warning LNK4221: This object file does not define any previously undefined public symbols, +// so it will not be used by any link operation that consumes this library +// +//#include "InvertFilter.h" From 4a08329d22cd094b984622ab575a898b6a1de78b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 1 Nov 2015 12:10:42 -0800 Subject: [PATCH 0601/1003] fix unix warnings for comparing enums to enums --- .../src/input-plugins/SDL2Manager.cpp | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp index ec2fa2ed07..c6f5491a5a 100644 --- a/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +++ b/libraries/input-plugins/src/input-plugins/SDL2Manager.cpp @@ -20,27 +20,27 @@ #ifdef HAVE_SDL2 static_assert( - controller::A == SDL_CONTROLLER_BUTTON_A && - controller::B == SDL_CONTROLLER_BUTTON_B && - controller::X == SDL_CONTROLLER_BUTTON_X && - controller::Y == SDL_CONTROLLER_BUTTON_Y && - controller::BACK == SDL_CONTROLLER_BUTTON_BACK && - controller::GUIDE == SDL_CONTROLLER_BUTTON_GUIDE && - controller::START == SDL_CONTROLLER_BUTTON_START && - controller::LS == SDL_CONTROLLER_BUTTON_LEFTSTICK && - controller::RS == SDL_CONTROLLER_BUTTON_RIGHTSTICK && - controller::LB == SDL_CONTROLLER_BUTTON_LEFTSHOULDER && - controller::RB == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER && - controller::DU == SDL_CONTROLLER_BUTTON_DPAD_UP && - controller::DD == SDL_CONTROLLER_BUTTON_DPAD_DOWN && - controller::DL == SDL_CONTROLLER_BUTTON_DPAD_LEFT && - controller::DR == SDL_CONTROLLER_BUTTON_DPAD_RIGHT && - controller::LX == SDL_CONTROLLER_AXIS_LEFTX && - controller::LY == SDL_CONTROLLER_AXIS_LEFTY && - controller::RX == SDL_CONTROLLER_AXIS_RIGHTX && - controller::RY == SDL_CONTROLLER_AXIS_RIGHTY && - controller::LT == SDL_CONTROLLER_AXIS_TRIGGERLEFT && - controller::RT == SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + (int)controller::A == (int)SDL_CONTROLLER_BUTTON_A && + (int)controller::B == (int)SDL_CONTROLLER_BUTTON_B && + (int)controller::X == (int)SDL_CONTROLLER_BUTTON_X && + (int)controller::Y == (int)SDL_CONTROLLER_BUTTON_Y && + (int)controller::BACK == (int)SDL_CONTROLLER_BUTTON_BACK && + (int)controller::GUIDE == (int)SDL_CONTROLLER_BUTTON_GUIDE && + (int)controller::START == (int)SDL_CONTROLLER_BUTTON_START && + (int)controller::LS == (int)SDL_CONTROLLER_BUTTON_LEFTSTICK && + (int)controller::RS == (int)SDL_CONTROLLER_BUTTON_RIGHTSTICK && + (int)controller::LB == (int)SDL_CONTROLLER_BUTTON_LEFTSHOULDER && + (int)controller::RB == (int)SDL_CONTROLLER_BUTTON_RIGHTSHOULDER && + (int)controller::DU == (int)SDL_CONTROLLER_BUTTON_DPAD_UP && + (int)controller::DD == (int)SDL_CONTROLLER_BUTTON_DPAD_DOWN && + (int)controller::DL == (int)SDL_CONTROLLER_BUTTON_DPAD_LEFT && + (int)controller::DR == (int)SDL_CONTROLLER_BUTTON_DPAD_RIGHT && + (int)controller::LX == (int)SDL_CONTROLLER_AXIS_LEFTX && + (int)controller::LY == (int)SDL_CONTROLLER_AXIS_LEFTY && + (int)controller::RX == (int)SDL_CONTROLLER_AXIS_RIGHTX && + (int)controller::RY == (int)SDL_CONTROLLER_AXIS_RIGHTY && + (int)controller::LT == (int)SDL_CONTROLLER_AXIS_TRIGGERLEFT && + (int)controller::RT == (int)SDL_CONTROLLER_AXIS_TRIGGERRIGHT, "SDL2 equvalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h"); #endif From e0ac48fe27b442efb7ef1a28ac4ce130b5aedb2a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 1 Nov 2015 11:28:38 -0800 Subject: [PATCH 0602/1003] PR comments --- BUILD.md | 17 +++++++++++++---- cmake/macros/TargetZlib.cmake | 11 ++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/BUILD.md b/BUILD.md index 7ad2ddeee0..72dfa5725e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -5,15 +5,24 @@ * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) -* [zlib](http://www.zlib.net/) ####CMake External Project Dependencies +* [boostconfig](https://github.com/boostorg/config) ~> 1.58 * [Bullet Physics Engine](https://code.google.com/p/bullet/downloads/list) ~> 2.82 -* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 +* [Faceshift](http://www.faceshift.com/) ~> 4.3 +* [GLEW](http://glew.sourceforge.net/) * [glm](http://glm.g-truc.net/0.9.5/index.html) ~> 0.9.5.4 * [gverb](https://github.com/highfidelity/gverb) +* [Oculus SDK](https://developer.oculus.com/downloads/) ~> 0.6 (Win32) / 0.5 (Mac / Linux) +* [oglplus](http://oglplus.org/) ~> 0.63 +* [OpenVR](https://github.com/ValveSoftware/openvr) ~> 0.91 (Win32 only) +* [Polyvox](http://www.volumesoffun.com/) ~> 0.2.1 * [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3 +* [soxr](http://soxr.sourceforge.net) ~> 0.1.1 +* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 +* [Sixense](http://sixense.com/) ~> 071615 +* [zlib](http://www.zlib.net/) ~> 1.28 (Win32 only) The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project. @@ -59,7 +68,7 @@ For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generat ####Finding Dependencies -The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DGET_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies). +The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies). You can point our [Cmake find modules](cmake/modules/) to the correct version of dependencies by setting one of the three following variables to the location of the correct version of the dependency. @@ -73,5 +82,5 @@ In the examples below the variable $NAME would be replaced by the name of the de ####Devices -You can support external input/output devices such as Oculus Rift, Leap Motion, Faceshift, MIDI, Razr Hydra and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. +You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. diff --git a/cmake/macros/TargetZlib.cmake b/cmake/macros/TargetZlib.cmake index 11eb8c132e..3eb0322ea0 100644 --- a/cmake/macros/TargetZlib.cmake +++ b/cmake/macros/TargetZlib.cmake @@ -6,12 +6,17 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_ZLIB) + if (WIN32) add_dependency_external_projects(zlib) - add_paths_to_fixup_libs(${ZLIB_DLL_PATH}) - else() - find_package(ZLIB REQUIRED) endif() + + find_package(ZLIB REQUIRED) + + if (WIN32) + add_paths_to_fixup_libs(${ZLIB_DLL_PATH}) + endif() + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES}) endmacro() From 13e805ac15f4ccf8ccefc51e9d722344adc8b8e4 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 1 Nov 2015 13:18:53 -0800 Subject: [PATCH 0603/1003] replace copy_resolved_item_into_bundle with a version that doesn't report warnings for a status case --- cmake/templates/FixupBundlePostBuild.cmake.in | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index 7d98e9796c..4afe4de403 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -10,5 +10,35 @@ # include(BundleUtilities) + + +# replace copy_resolved_item_into_bundle +# +# The official version of copy_resolved_item_into_bundle will print out a "warning:" when +# the resolved item matches the resolved embedded item. This not not really an issue that +# should rise to the level of a "warning" so we replace this message with a "status:" +# +function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) + if(WIN32) + # ignore case on Windows + string(TOLOWER "${resolved_item}" resolved_item_compare) + string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare) + else() + set(resolved_item_compare "${resolved_item}") + set(resolved_embedded_item_compare "${resolved_embedded_item}") + endif() + + if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}") + # this is our only change from the original version + message(STATUS "status: resolved_item == resolved_embedded_item - not copying...") + else() + #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") + if(UNIX AND NOT APPLE) + file(RPATH_REMOVE FILE "${resolved_embedded_item}") + endif() + endif() +endfunction() + message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@") fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@") \ No newline at end of file From 1e47a536031829dfad3a527b428447f3df96c0af Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 1 Nov 2015 14:34:03 -0800 Subject: [PATCH 0604/1003] Fixing mac oculus crash --- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 8 ++++++++ plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h | 1 + 2 files changed, 9 insertions(+) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 5a253cdbbf..0120bcf2aa 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -190,6 +190,14 @@ void OculusLegacyDisplayPlugin::deactivate() { ovr_Shutdown(); } +// DLL based display plugins MUST initialize GLEW inside the DLL code. +void OculusLegacyDisplayPlugin::customizeContext() { + glewExperimental = true; + GLenum err = glewInit(); + glGetError(); + WindowOpenGLDisplayPlugin::customizeContext(); +} + void OculusLegacyDisplayPlugin::preDisplay() { _window->makeCurrent(); } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index 2e95cee9bb..6e3f864aee 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -35,6 +35,7 @@ public: virtual glm::mat4 getHeadPose() const override; protected: + virtual void customizeContext() override; virtual void preRender() override; virtual void preDisplay() override; virtual void display(GLuint finalTexture, const glm::uvec2& sceneSize) override; From b9d60e803b566dbcd8ae00c98202d8fc96936a20 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 1 Nov 2015 15:13:28 -0800 Subject: [PATCH 0605/1003] fix mac warning --- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 0120bcf2aa..d8dc3667ae 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -193,7 +193,7 @@ void OculusLegacyDisplayPlugin::deactivate() { // DLL based display plugins MUST initialize GLEW inside the DLL code. void OculusLegacyDisplayPlugin::customizeContext() { glewExperimental = true; - GLenum err = glewInit(); + glewInit(); glGetError(); WindowOpenGLDisplayPlugin::customizeContext(); } From 92bcd9ca9eb583252b63fbcd0c397390521a5779 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Sun, 1 Nov 2015 15:48:01 -0800 Subject: [PATCH 0606/1003] make oculus plugin use static glew --- plugins/oculus/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 62999cbb7e..fe1a87d6b6 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -8,6 +8,9 @@ if (WIN32) + # we're using static GLEW, so define GLEW_STATIC + add_definitions(-DGLEW_STATIC) + set(TARGET_NAME oculus) setup_hifi_plugin() link_hifi_libraries(shared gl plugins display-plugins) From fc63fa6f7b963acc1c6c481a35009c8a5b9e939b Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sun, 1 Nov 2015 18:55:43 -0800 Subject: [PATCH 0607/1003] Update comments, and don't continue through collisions after finding one for our avatar. --- interface/src/avatar/AvatarManager.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b0da8faeca..4b9bfd21a3 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -286,10 +286,10 @@ void AvatarManager::handleOutgoingChanges(const VectorOfMotionStates& motionStat void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents) { for (Collision collision : collisionEvents) { - // TODO: Current physics uses null idA or idB for non-entities. The plan is to handle MOTIONSTATE_TYPE_AVATAR, - // and then MOTIONSTATE_TYPE_MYAVATAR. As it is, this code only covers the case of my avatar (in which case one - // if the ids will be null), and the behavior for other avatars is not specified. This has to be fleshed - // out as soon as we use the new motionstates. + // TODO: The plan is to handle MOTIONSTATE_TYPE_AVATAR, and then MOTIONSTATE_TYPE_MYAVATAR. As it is, other + // people's avatars will have an id that doesn't match any entities, and one's own avatar will have + // an id of null. Thus this code handles any collision in which one of the participating objects is + // my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.) if (collision.idA.isNull() || collision.idB.isNull()) { MyAvatar* myAvatar = getMyAvatar(); const QString& collisionSoundURL = myAvatar->getCollisionSoundURL(); @@ -299,9 +299,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION); if (!isSound) { - // TODO: When the new motion states are used, we'll probably break from the whole loop as soon as we hit our own avatar - // (regardless of isSound), because other users should inject for their own avatars. - continue; + return; // No sense iterating for others. We only have one avatar. } // Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for. const float energy = velocityChange * velocityChange; @@ -314,7 +312,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition()); myAvatar->collisionWithEntity(collision); - } + return; } } } } From e79ebdb8aa88157fd3be5233ca7fe5a9a865661a Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sun, 1 Nov 2015 19:47:13 -0800 Subject: [PATCH 0608/1003] cleanup syntax --- examples/color_busters_game/createColorBusterCubes.js | 6 +++--- examples/color_busters_game/createColorBusterWand.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/color_busters_game/createColorBusterCubes.js b/examples/color_busters_game/createColorBusterCubes.js index ad7b8b5307..2bc14e5a01 100644 --- a/examples/color_busters_game/createColorBusterCubes.js +++ b/examples/color_busters_game/createColorBusterCubes.js @@ -4,7 +4,7 @@ var CUBE_DIMENSIONS = { z: 1 }; -var NUMBER_OF_CUBES_PER_SIDE: 20; +var NUMBER_OF_CUBES_PER_SIDE= 20; var STARTING_CORNER_POSITION = { x: 0, @@ -62,7 +62,7 @@ function createColorBusterCube(row, column, vertical) { x: row, y: vertical, z: column - } + }, userData: JSON.stringify({ hifiColorBusterCubeKey: { originalColorName: startingColor[0], @@ -76,7 +76,7 @@ function createColorBusterCube(row, column, vertical) { function createBoard() { var vertical; - for (vertical = 0; vertical === NUMBER_OF_CUBES_PER_SIDE) { + for (vertical = 0; vertical === NUMBER_OF_CUBES_PER_SIDE;vertical++) { var row; var column; //create a single layer diff --git a/examples/color_busters_game/createColorBusterWand.js b/examples/color_busters_game/createColorBusterWand.js index 68e6ecfee5..4af5306177 100644 --- a/examples/color_busters_game/createColorBusterWand.js +++ b/examples/color_busters_game/createColorBusterWand.js @@ -44,7 +44,7 @@ function createColorBusterWand() { userData: JSON.stringify({ hifiColorBusterWandKey: { owner: MyAvatar.sessionUUID, - currentColor: startingColor[1] + currentColor: startingColor[1], originalColorName: startingColor[0], colorLocked: false } From 70b9aa5ff198f074fd9b5ae1989bec1fff033626 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 07:42:52 -0800 Subject: [PATCH 0609/1003] remove right click context menu until we can make it work with camera move better --- interface/resources/controllers/keyboardMouse.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 9c3ba79d76..8af6b1dc98 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -81,7 +81,6 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, - { "from": "Keyboard.T", "to": "Actions.ACTION2" }, - { "from": "Keyboard.RightMouseClick", "to": "Actions.ContextMenu" } + { "from": "Keyboard.T", "to": "Actions.ACTION2" } ] } From 017e42bbc25478ab9ad8c1f733ea617ed42cccdf Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 08:27:51 -0800 Subject: [PATCH 0610/1003] Update sixense SDK --- cmake/externals/sixense/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index 07cf1c1163..8ae18e638a 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -6,8 +6,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip - URL_MD5 752a3901f334124e9cffc2ba4136ef7d + URL https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip + URL_MD5 93c3a6795cce777a0f472b09532935f1 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" From b00f572e1a278e4eadc7b0947fe867ebdf0658c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 09:21:15 -0800 Subject: [PATCH 0611/1003] add mouseClicked inputs vs mouseButton inputs, make right-click work without right button drag --- .../resources/controllers/keyboardMouse.json | 19 +++++----- .../src/input-plugins/KeyboardMouseDevice.cpp | 37 +++++++++++++++---- .../src/input-plugins/KeyboardMouseDevice.h | 7 +++- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 8af6b1dc98..f99472b0e9 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -4,8 +4,8 @@ { "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.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.D", "when": "Keyboard.RightMouseButton", "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" }, @@ -13,7 +13,7 @@ { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], + "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseButton" ], "to": "Actions.StepYaw", "filters": [ @@ -46,7 +46,7 @@ }, { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, - "when": "Keyboard.RightMouseClick", + "when": "Keyboard.RightMouseButton", "to": "Actions.Yaw" }, @@ -55,8 +55,8 @@ { "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.RightMouseButton", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.Right", "when": "Keyboard.RightMouseButton", "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" }, @@ -68,8 +68,8 @@ { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, - { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, - { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" }, + { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP" }, + { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, @@ -81,6 +81,7 @@ { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, { "from": "Keyboard.R", "to": "Actions.ACTION1" }, - { "from": "Keyboard.T", "to": "Actions.ACTION2" } + { "from": "Keyboard.T", "to": "Actions.ACTION2" }, + { "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" } ] } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 9cd510c521..42081aca30 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -59,11 +59,25 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic // key pressed again ? without catching the release event ? } _lastCursor = event->pos(); + _mousePressAt = event->pos(); + + eraseMouseClicked(); } void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { auto input = makeInput((Qt::MouseButton) event->button()); _buttonPressedMap.erase(input.getChannel()); + + if (_mousePressAt == event->pos()) { + auto input = makeInput((Qt::MouseButton) event->button(), true); // clicked + auto result = _buttonPressedMap.insert(input.getChannel()); + } +} + +void KeyboardMouseDevice::eraseMouseClicked() { + _buttonPressedMap.erase(makeInput(Qt::LeftButton, true).getChannel()); + _buttonPressedMap.erase(makeInput(Qt::MiddleButton, true).getChannel()); + _buttonPressedMap.erase(makeInput(Qt::RightButton, true).getChannel()); } void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { @@ -77,6 +91,8 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device _axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _lastCursor = currentPos; + + eraseMouseClicked(); } void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { @@ -138,14 +154,17 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const { +controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool quickClicked) const { switch (code) { case Qt::LeftButton: - return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_QUICK_CLICK : + MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_QUICK_CLICK : + MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_QUICK_CLICK : + MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: return controller::Input(); }; @@ -182,9 +201,13 @@ controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { 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), "LeftMouseClick")); - availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick")); - availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick")); + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseButton")); + + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton, true), "LeftMouseClicked")); + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton, true), "MiddleMouseClicked")); + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton, true), "RightMouseClicked")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight")); availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft")); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 1ff77d2dce..75a1159b92 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -37,6 +37,9 @@ public: MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE, + MOUSE_BUTTON_LEFT_QUICK_CLICK, + MOUSE_BUTTON_RIGHT_QUICK_CLICK, + MOUSE_BUTTON_MIDDLE_QUICK_CLICK, }; enum MouseAxisChannel { @@ -83,6 +86,7 @@ public: void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); void mousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); + void eraseMouseClicked(); void touchBeginEvent(const QTouchEvent* event); void touchEndEvent(const QTouchEvent* event); @@ -92,7 +96,7 @@ public: // Let's make it easy for Qt because we assume we love Qt forever controller::Input makeInput(Qt::Key code) const; - controller::Input makeInput(Qt::MouseButton code) const; + controller::Input makeInput(Qt::MouseButton code, bool quickClicked = false) const; controller::Input makeInput(MouseAxisChannel axis) const; controller::Input makeInput(TouchAxisChannel axis) const; controller::Input makeInput(TouchButtonChannel button) const; @@ -101,6 +105,7 @@ public: protected: QPoint _lastCursor; + QPoint _mousePressAt; glm::vec2 _lastTouch; bool _isTouching = false; From c3a78ed151a60dd9530765e631f6270ca4e1b887 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 09:27:35 -0800 Subject: [PATCH 0612/1003] cleanup --- .../src/input-plugins/KeyboardMouseDevice.cpp | 12 +++++++----- .../src/input-plugins/KeyboardMouseDevice.h | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 42081aca30..247e08ada1 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -68,9 +68,11 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int dev auto input = makeInput((Qt::MouseButton) event->button()); _buttonPressedMap.erase(input.getChannel()); + // if we pressed and released at the same location, then create a "_CLICKED" input for this button + // we might want to add some small tolerance to this so if you do a small drag it still counts as + // a clicked. if (_mousePressAt == event->pos()) { - auto input = makeInput((Qt::MouseButton) event->button(), true); // clicked - auto result = _buttonPressedMap.insert(input.getChannel()); + _buttonPressedMap.insert(makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } @@ -157,13 +159,13 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool quickClicked) const { switch (code) { case Qt::LeftButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_QUICK_CLICK : + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_CLICKED : MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_QUICK_CLICK : + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_CLICKED : MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_QUICK_CLICK : + return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_CLICKED : MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: return controller::Input(); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 75a1159b92..b6681e2ae0 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -37,9 +37,9 @@ public: MOUSE_BUTTON_LEFT = KEYBOARD_LAST + 1, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE, - MOUSE_BUTTON_LEFT_QUICK_CLICK, - MOUSE_BUTTON_RIGHT_QUICK_CLICK, - MOUSE_BUTTON_MIDDLE_QUICK_CLICK, + MOUSE_BUTTON_LEFT_CLICKED, + MOUSE_BUTTON_RIGHT_CLICKED, + MOUSE_BUTTON_MIDDLE_CLICKED, }; enum MouseAxisChannel { From d9dd045886fba3a416eda545a74a3be5bb5d55ad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 09:29:09 -0800 Subject: [PATCH 0613/1003] better parameter name --- .../src/input-plugins/KeyboardMouseDevice.cpp | 8 ++++---- .../input-plugins/src/input-plugins/KeyboardMouseDevice.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 247e08ada1..2157a3a010 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -156,16 +156,16 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool quickClicked) const { +controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clicked) const { switch (code) { case Qt::LeftButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_LEFT_CLICKED : + return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_LEFT_CLICKED : MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); case Qt::RightButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_RIGHT_CLICKED : + return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_RIGHT_CLICKED : MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); case Qt::MiddleButton: - return controller::Input(_deviceID, quickClicked ? MOUSE_BUTTON_MIDDLE_CLICKED : + return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_MIDDLE_CLICKED : MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); default: return controller::Input(); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b6681e2ae0..5d86821db1 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -96,7 +96,7 @@ public: // Let's make it easy for Qt because we assume we love Qt forever controller::Input makeInput(Qt::Key code) const; - controller::Input makeInput(Qt::MouseButton code, bool quickClicked = false) const; + controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; controller::Input makeInput(MouseAxisChannel axis) const; controller::Input makeInput(TouchAxisChannel axis) const; controller::Input makeInput(TouchButtonChannel button) const; From b067abe2d9f74a93afda0fe858b2a71adccf991c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 2 Nov 2015 09:58:23 -0800 Subject: [PATCH 0614/1003] Added name to basketball hoop in reset scripts --- unpublishedScripts/hiddenEntityReset.js | 1 + unpublishedScripts/masterReset.js | 1 + 2 files changed, 2 insertions(+) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 43d23e4b3c..015157fd26 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -944,6 +944,7 @@ var hoop = Entities.addEntity({ type: "Model", modelURL: hoopURL, + name: "Basketball Hoop", position: position, rotation: rotation, shapeType: 'compound', diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 74eb3c85ac..2e7a7ee2e7 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -921,6 +921,7 @@ MasterReset = function() { var hoop = Entities.addEntity({ type: "Model", + name: "Basketball Hoop", modelURL: hoopURL, position: position, rotation: rotation, From c381da24a7a7871bc9e7ccf6093979a6a08a27c1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 10:42:06 -0800 Subject: [PATCH 0615/1003] get interface building on my linux box --- libraries/gl/src/gl/GlWindow.h | 2 +- .../oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/gl/src/gl/GlWindow.h b/libraries/gl/src/gl/GlWindow.h index d62d891c12..4956177725 100644 --- a/libraries/gl/src/gl/GlWindow.h +++ b/libraries/gl/src/gl/GlWindow.h @@ -11,7 +11,7 @@ #define hifi_GlWindow_h #include -#include +#include class QOpenGLContext; class QOpenGLDebugLogger; diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index d8dc3667ae..3898d586ad 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -9,15 +9,15 @@ #include -#include -#include +#include +#include #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include From fd557c4e529ef06b36500d2e73d399bb38a103c9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 30 Oct 2015 18:59:33 -0700 Subject: [PATCH 0616/1003] route HMD-following through the physics simulation --- interface/src/avatar/MyAvatar.cpp | 191 +++++++++++------------- interface/src/avatar/MyAvatar.h | 15 +- libraries/shared/src/AtRestDetector.cpp | 24 ++- libraries/shared/src/AtRestDetector.h | 11 +- 4 files changed, 125 insertions(+), 116 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9dcd77c689..e3528f3287 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -157,6 +157,8 @@ void MyAvatar::reset(bool andReload) { // Reset dynamic state. _wasPushing = _isPushing = _isBraking = _billboardValid = false; _isFollowingHMD = false; + _hmdFollowVelocity = Vectors::ZERO; + _hmdFollowSpeed = 0.0f; _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -332,25 +334,6 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrix; } -// returns true if pos is OUTSIDE of the vertical capsule -// where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad. -static bool pointIsOutsideCapsule(const glm::vec3& pos, float capsuleLen, float capsuleRad) { - const float halfCapsuleLen = capsuleLen / 2.0f; - if (fabs(pos.y) <= halfCapsuleLen) { - // cylinder check for middle capsule - glm::vec2 horizPos(pos.x, pos.z); - return glm::length(horizPos) > capsuleRad; - } else if (pos.y > halfCapsuleLen) { - glm::vec3 center(0.0f, halfCapsuleLen, 0.0f); - return glm::length(center - pos) > capsuleRad; - } else if (pos.y < halfCapsuleLen) { - glm::vec3 center(0.0f, -halfCapsuleLen, 0.0f); - return glm::length(center - pos) > capsuleRad; - } else { - return false; - } -} - // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. @@ -359,102 +342,58 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorMatrix = hmdSensorMatrix; _hmdSensorPosition = extractTranslation(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); +} - // calc deltaTime - auto now = usecTimestampNow(); - auto deltaUsecs = now - _lastUpdateFromHMDTime; - _lastUpdateFromHMDTime = now; - double actualDeltaTime = (double)deltaUsecs / (double)USECS_PER_SECOND; - const float BIGGEST_DELTA_TIME_SECS = 0.25f; - float deltaTime = glm::clamp((float)actualDeltaTime, 0.0f, BIGGEST_DELTA_TIME_SECS); - - bool hmdIsAtRest = _hmdAtRestDetector.update(deltaTime, _hmdSensorPosition, _hmdSensorOrientation); - - // It can be more accurate/smooth to use velocity rather than position, - // but some modes (e.g., hmd standing) update position without updating velocity. - // So, let's create our own workingVelocity from the worldPosition... - glm::vec3 positionDelta = getPosition() - _lastPosition; - glm::vec3 workingVelocity = positionDelta / deltaTime; - _lastPosition = getPosition(); - - const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec - const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec +void MyAvatar::updateHMDFollowVelocity() { bool isMoving; if (_lastIsMoving) { - isMoving = glm::length(workingVelocity) >= MOVE_EXIT_SPEED_THRESHOLD; + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; } else { - isMoving = glm::length(workingVelocity) > MOVE_ENTER_SPEED_THRESHOLD; + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; } bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; _lastIsMoving = isMoving; - if (shouldFollowHMD() || hmdIsAtRest || justStartedMoving) { - beginFollowingHMD(); - } - - followHMD(deltaTime); -} - -glm::vec3 MyAvatar::getHMDCorrectionVelocity() const { - // TODO: impelement this - return Vectors::ZERO; -} - -void MyAvatar::beginFollowingHMD() { - // begin homing toward derived body position. - if (!_isFollowingHMD) { + bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); + if (hmdIsAtRest || justStartedMoving) { _isFollowingHMD = true; - _followHMDAlpha = 0.0f; } -} -bool MyAvatar::shouldFollowHMD() const { - if (!_isFollowingHMD) { - // define a vertical capsule - const float FOLLOW_HMD_CAPSULE_RADIUS = 0.2f; // meters - const float FOLLOW_HMD_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. - - // detect if the derived body position is outside of a capsule around the _bodySensorMatrix - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); - glm::vec3 localPoint = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - return pointIsOutsideCapsule(localPoint, FOLLOW_HMD_CAPSULE_LENGTH, FOLLOW_HMD_CAPSULE_RADIUS); + // compute offset to body's target position (in sensor-frame) + auto sensorBodyMatrix = deriveBodyFromHMDSensor(); + _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); + if (_hmdFollowOffset.y < 0.0f) { + // don't pull the body DOWN to match the target (allow animation system to squat) + _hmdFollowOffset.y = 0.0f; } - return false; -} -void MyAvatar::followHMD(float deltaTime) { - if (_isFollowingHMD) { - - const float FOLLOW_HMD_DURATION = 0.5f; // seconds - - auto newBodySensorMatrix = deriveBodyFromHMDSensor(); - auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; - glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix); - glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); - - _followHMDAlpha += (1.0f / FOLLOW_HMD_DURATION) * deltaTime; - - if (_followHMDAlpha >= 1.0f) { - _isFollowingHMD = false; - nextAttitude(worldBodyPos, worldBodyRot); - _bodySensorMatrix = newBodySensorMatrix; - } else { - // interp position toward the desired pos - glm::vec3 pos = lerp(getPosition(), worldBodyPos, _followHMDAlpha); - glm::quat rot = glm::normalize(safeMix(getOrientation(), worldBodyRot, _followHMDAlpha)); - nextAttitude(pos, rot); - - // interp sensor matrix toward desired - glm::vec3 nextBodyPos = extractTranslation(newBodySensorMatrix); - glm::quat nextBodyRot = glm::normalize(glm::quat_cast(newBodySensorMatrix)); - glm::vec3 prevBodyPos = extractTranslation(_bodySensorMatrix); - glm::quat prevBodyRot = glm::normalize(glm::quat_cast(_bodySensorMatrix)); - pos = lerp(prevBodyPos, nextBodyPos, _followHMDAlpha); - rot = glm::normalize(safeMix(prevBodyRot, nextBodyRot, _followHMDAlpha)); - _bodySensorMatrix = createMatFromQuatAndPos(rot, pos); + bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); + if (!needNewFollowSpeed) { + // check to see if offset has exceeded its threshold + float distance = glm::length(_hmdFollowOffset); + const float MAX_HMD_HIP_SHIFT = 0.2f; + if (distance > MAX_HMD_HIP_SHIFT) { + _isFollowingHMD = true; + needNewFollowSpeed = true; } } + if (_isFollowingHMD) { + // only bother to rotate into world frame if we're following + glm::quat sensorToWorldRotation = extractRotation(_sensorToWorldMatrix); + _hmdFollowOffset = sensorToWorldRotation * _hmdFollowOffset; + } + if (needNewFollowSpeed) { + // compute new velocity that will be used to resolve offset of hips from body + const float FOLLOW_HMD_DURATION = 0.5f; // seconds + _hmdFollowVelocity = (_hmdFollowOffset / FOLLOW_HMD_DURATION); + _hmdFollowSpeed = glm::length(_hmdFollowVelocity); + } else if (_isFollowingHMD) { + // compute new velocity (but not new speed) + _hmdFollowVelocity = _hmdFollowSpeed * glm::normalize(_hmdFollowOffset); + } } // best called at end of main loop, just before rendering. @@ -1356,7 +1295,8 @@ void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); _characterController.setTargetVelocity(getTargetVelocity()); _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); - _characterController.setHMDVelocity(getHMDCorrectionVelocity()); + updateHMDFollowVelocity(); + _characterController.setHMDVelocity(_hmdFollowVelocity); } void MyAvatar::harvestResultsFromPhysicsSimulation() { @@ -1364,9 +1304,54 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() { glm::quat orientation = getOrientation(); _characterController.getAvatarPositionAndOrientation(position, orientation); nextAttitude(position, orientation); - setVelocity(_characterController.getLinearVelocity()); - // TODO: harvest HMD shift here - //glm::vec3 hmdShift = _characterController.getHMDShift(); + if (_isFollowingHMD) { + setVelocity(_characterController.getLinearVelocity() + _hmdFollowVelocity); + glm::vec3 hmdShift = _characterController.getHMDShift(); + adjustSensorTransform(hmdShift); + } else { + setVelocity(_characterController.getLinearVelocity()); + } +} + +void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) { + // compute blendFactor of latest hmdShift + // which we'll use to blend the rotation part + float blendFactor = 1.0f; + float shiftLength = glm::length(hmdShift); + if (shiftLength > 1.0e-5f) { + float offsetLength = glm::length(_hmdFollowOffset); + if (offsetLength > shiftLength) { + blendFactor = shiftLength / offsetLength; + } + } + + auto newBodySensorMatrix = deriveBodyFromHMDSensor(); + auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; + glm::quat finalBodyRotation = glm::normalize(glm::quat_cast(worldBodyMatrix)); + if (blendFactor >= 0.99f) { + // the "adjustment" is more or less complete so stop following + _isFollowingHMD = false; + _hmdFollowSpeed = 0.0f; + // and slam the body's transform anyway to eliminate any slight errors + glm::vec3 finalBodyPosition = extractTranslation(worldBodyMatrix); + nextAttitude(finalBodyPosition, finalBodyRotation); + _bodySensorMatrix = newBodySensorMatrix; + } else { + // physics already did the positional blending for us + glm::vec3 newBodyPosition = getPosition(); + // but the rotational part must be done manually + glm::quat newBodyRotation = glm::normalize(safeMix(getOrientation(), finalBodyRotation, blendFactor)); + nextAttitude(newBodyPosition, newBodyRotation); + + // interp sensor matrix toward the desired + glm::vec3 prevPosition = extractTranslation(_bodySensorMatrix); + glm::quat prevRotation = glm::normalize(glm::quat_cast(_bodySensorMatrix)); + glm::vec3 nextPosition = extractTranslation(newBodySensorMatrix); + glm::quat nextRotation = glm::normalize(glm::quat_cast(newBodySensorMatrix)); + _bodySensorMatrix = createMatFromQuatAndPos( + glm::normalize(safeMix(prevRotation, nextRotation, blendFactor)), + lerp(prevPosition, nextPosition, blendFactor)); + } } QString MyAvatar::getScriptedMotorFrame() const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 38c2c00382..ec33d22d8d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -101,8 +101,6 @@ public: // as it moves through the world. void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); - glm::vec3 getHMDCorrectionVelocity() const; - // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. @@ -211,6 +209,7 @@ public: void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(); + void adjustSensorTransform(glm::vec3 hmdShift); const QString& getCollisionSoundURL() { return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); @@ -316,9 +315,10 @@ private: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } - void beginFollowingHMD(); - bool shouldFollowHMD() const; - void followHMD(float deltaTime); + //void beginFollowingHMD(); + //bool shouldFollowHMD() const; + //void followHMD(float deltaTime); + void updateHMDFollowVelocity(); bool cameraInsideHead() const; @@ -395,6 +395,9 @@ private: // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. glm::mat4 _sensorToWorldMatrix; + glm::vec3 _hmdFollowOffset { Vectors::ZERO }; + glm::vec3 _hmdFollowVelocity { Vectors::ZERO }; + float _hmdFollowSpeed { 0.0f }; bool _goToPending; glm::vec3 _goToPosition; @@ -415,9 +418,7 @@ private: bool _isFollowingHMD { false }; float _followHMDAlpha { 0.0f }; - quint64 _lastUpdateFromHMDTime { usecTimestampNow() }; AtRestDetector _hmdAtRestDetector; - glm::vec3 _lastPosition; bool _lastIsMoving { false }; quint64 _lastStepPulse { 0 }; bool _pulseUpdate { false }; diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index 5e623a005c..799450df94 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -1,5 +1,19 @@ +// +// AtRestDetector.cpp +// libraries/shared/src +// +// Created by Anthony Thibault on 10/6/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 +// + #include "AtRestDetector.h" + +#include "NumericalConstants.h" #include "SharedLogging.h" +#include "SharedUtil.h" AtRestDetector::AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation) { reset(startPosition, startRotation); @@ -12,9 +26,14 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star glm::quat ql = glm::log(startRotation); _quatLogAverage = glm::vec3(ql.x, ql.y, ql.z); _quatLogVariance = 0.0f; + _lastUpdateTime = usecTimestampNow(); + _isAtRest = false; } -bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat& rotation) { +bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { + uint64_t now = usecTimestampNow(); + float dt = (float)(_lastUpdateTime - now) / (float)USECS_PER_SECOND; + _lastUpdateTime = now; const float TAU = 1.0f; float delta = glm::min(dt / TAU, 1.0f); @@ -37,5 +56,6 @@ bool AtRestDetector::update(float dt, const glm::vec3& position, const glm::quat const float POSITION_VARIANCE_THRESHOLD = 0.001f; const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; - return _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; + _isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; + return _isAtRest; } diff --git a/libraries/shared/src/AtRestDetector.h b/libraries/shared/src/AtRestDetector.h index d82e54a692..525156de65 100644 --- a/libraries/shared/src/AtRestDetector.h +++ b/libraries/shared/src/AtRestDetector.h @@ -21,14 +21,17 @@ public: void reset(const glm::vec3& startPosition, const glm::quat& startRotation); // returns true if object is at rest, dt in assumed to be seconds. - bool update(float dt, const glm::vec3& position, const glm::quat& startRotation); + bool update(const glm::vec3& position, const glm::quat& startRotation); + + bool isAtRest() const { return _isAtRest; } protected: glm::vec3 _positionAverage; - float _positionVariance; - glm::vec3 _quatLogAverage; - float _quatLogVariance; + uint64_t _lastUpdateTime { 0 }; + float _positionVariance { 0.0f }; + float _quatLogVariance { 0.0f }; + bool _isAtRest { false }; }; #endif From 9799693135189f176e4b7009ac7143919328ecde Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 2 Nov 2015 09:17:51 -0800 Subject: [PATCH 0617/1003] remove extra whitespace --- interface/src/avatar/MyAvatar.cpp | 10 +++++----- libraries/shared/src/AtRestDetector.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e3528f3287..95a494e4fd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -250,7 +250,7 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("transform"); bool stepAction = false; - // When there are no step values, we zero out the last step pulse. + // 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) { @@ -1297,7 +1297,7 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); updateHMDFollowVelocity(); _characterController.setHMDVelocity(_hmdFollowVelocity); -} +} void MyAvatar::harvestResultsFromPhysicsSimulation() { glm::vec3 position = getPosition(); @@ -1338,7 +1338,7 @@ void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) { _bodySensorMatrix = newBodySensorMatrix; } else { // physics already did the positional blending for us - glm::vec3 newBodyPosition = getPosition(); + glm::vec3 newBodyPosition = getPosition(); // but the rotational part must be done manually glm::quat newBodyRotation = glm::normalize(safeMix(getOrientation(), finalBodyRotation, blendFactor)); nextAttitude(newBodyPosition, newBodyRotation); @@ -1453,7 +1453,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl getHead()->renderLookAts(renderArgs); } - if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && + if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHandTargets)) { getHand()->renderHandTargets(renderArgs, true); } @@ -1932,7 +1932,7 @@ void MyAvatar::updateMotionBehaviorFromMenu() { QMetaObject::invokeMethod(this, "updateMotionBehaviorFromMenu"); return; } - + Menu* menu = Menu::getInstance(); if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) { _motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED; diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index 799450df94..d790e2b066 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -31,7 +31,7 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star } bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { - uint64_t now = usecTimestampNow(); + uint64_t now = usecTimestampNow(); float dt = (float)(_lastUpdateTime - now) / (float)USECS_PER_SECOND; _lastUpdateTime = now; const float TAU = 1.0f; From a545d770d888f12b42dbde32df275783afb02236 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 2 Nov 2015 11:06:17 -0800 Subject: [PATCH 0618/1003] fix bug preventing avatar motion in 2D mode --- interface/src/avatar/MyAvatar.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 95a494e4fd..3bf1b79902 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1295,8 +1295,13 @@ void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); _characterController.setTargetVelocity(getTargetVelocity()); _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); - updateHMDFollowVelocity(); - _characterController.setHMDVelocity(_hmdFollowVelocity); + if (qApp->isHMDMode()) { + updateHMDFollowVelocity(); + _characterController.setHMDVelocity(_hmdFollowVelocity); + } else { + _characterController.setHMDVelocity(Vectors::ZERO); + _isFollowingHMD = false; + } } void MyAvatar::harvestResultsFromPhysicsSimulation() { From 3019886344f9f6101f10d682b7d81ac8d04a63ac Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 2 Nov 2015 11:08:06 -0800 Subject: [PATCH 0619/1003] Removing basketball hoop from reset scripts Hoop will be saved in SVO instead --- unpublishedScripts/hiddenEntityReset.js | 44 ------------------------- unpublishedScripts/masterReset.js | 43 +----------------------- 2 files changed, 1 insertion(+), 86 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 015157fd26..f56c705ce4 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -114,7 +114,6 @@ createTargets(); createTargetResetter(); - createBasketballHoop(); createBasketballRack(); createBasketballResetter(); @@ -130,14 +129,11 @@ z: 503.49 }); - createSprayCan({ x: 549.7, y: 495.6, z: 503.91 }); - - } function deleteAllToys() { @@ -930,46 +926,6 @@ }); } - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - modelURL: hoopURL, - name: "Basketball Hoop", - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 2e7a7ee2e7..b138db163d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -10,7 +10,7 @@ //per script -/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketballHoop, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */ +/*global deleteAllToys, createAllToys, createGates, createPingPongBallGun, createFire, createPottedPlant, createCombinedArmChair, createBasketBall, createSprayCan, createDoll, createWand, createDice, createCat, deleteAllToys, createFlashlight, createBlocks, createMagballs, createLights */ var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js"); Script.include(utilitiesScript); @@ -87,7 +87,6 @@ MasterReset = function() { createTargets(); createTargetResetter(); - createBasketballHoop(); createBasketballRack(); createBasketballResetter(); @@ -908,46 +907,6 @@ MasterReset = function() { }); } - function createBasketballHoop() { - var position = { - x: 539.23, - y: 496.13, - z: 475.89 - }; - var rotation = Quat.fromPitchYawRollDegrees(0, 58.49, 0); - - var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; - var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; - - var hoop = Entities.addEntity({ - type: "Model", - name: "Basketball Hoop", - modelURL: hoopURL, - position: position, - rotation: rotation, - shapeType: 'compound', - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - dimensions: { - x: 1.89, - y: 3.99, - z: 3.79 - }, - compoundShapeURL: hoopCollisionHullURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - } - }) - }); - } - function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; From edf3e5b17a45f1042a461d40004ab557a51df6f5 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 10:51:51 -0800 Subject: [PATCH 0620/1003] Try using the VS2010 builds from the new Sixense SDKs --- cmake/externals/sixense/CMakeLists.txt | 30 ++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index 8ae18e638a..0cbca087a5 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -4,10 +4,22 @@ set(EXTERNAL_NAME sixense) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +#set(SIXENSE_URL "https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip") +#set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc") +#set(SIXENSE_NEW_LAYOUT 0) + +#set(SIXENSE_URL "https://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip") +#set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d") +#set(SIXENSE_NEW_LAYOUT 1) + +set(SIXENSE_URL "https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip") +set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1") +set(SIXENSE_NEW_LAYOUT 1) + ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip - URL_MD5 93c3a6795cce777a0f472b09532935f1 + URL ${SIXENSE_URL} + URL_MD5 ${SIXENSE_URL_MD5} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" @@ -30,8 +42,18 @@ if (WIN32) set(ARCH_SUFFIX "") endif() - set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) - add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll") + if (${SIXENSE_NEW_LAYOUT}) + # for 2015 SDKs (using the 2013 versions may be causing the crash) + set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/VS2010/release_dll") + set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2010/release_dll") + else() + # for the 2012 SDK + set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/release_dll") + set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll") + endif() + + set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE "${${EXTERNAL_NAME_UPPER}_LIB_PATH}/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) + add_paths_to_fixup_libs("${${EXTERNAL_NAME_UPPER}_DLL_PATH}") elseif(APPLE) From a77f888f1574589f802daa62b373499858e5d7cb Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 2 Nov 2015 11:29:17 -0800 Subject: [PATCH 0621/1003] more correct HMD offset correction velocity --- interface/src/avatar/MyAvatar.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3bf1b79902..00870c62c6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -365,15 +365,16 @@ void MyAvatar::updateHMDFollowVelocity() { // compute offset to body's target position (in sensor-frame) auto sensorBodyMatrix = deriveBodyFromHMDSensor(); _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); - if (_hmdFollowOffset.y < 0.0f) { + glm::vec3 truncatedOffset = _hmdFollowOffset; + if (truncatedOffset.y < 0.0f) { // don't pull the body DOWN to match the target (allow animation system to squat) - _hmdFollowOffset.y = 0.0f; + truncatedOffset.y = 0.0f; } bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); if (!needNewFollowSpeed) { // check to see if offset has exceeded its threshold - float distance = glm::length(_hmdFollowOffset); + float distance = glm::length(truncatedOffset); const float MAX_HMD_HIP_SHIFT = 0.2f; if (distance > MAX_HMD_HIP_SHIFT) { _isFollowingHMD = true; From b94258f9536dab302db52651d54b8e94b5ddfb59 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 11:55:50 -0800 Subject: [PATCH 0622/1003] add example of overriding default rightClick behavior --- examples/controllers/rightClickExample.js | 10 ++++++++++ .../controllers/src/controllers/UserInputMapper.cpp | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js new file mode 100644 index 0000000000..c3e6ea8f3d --- /dev/null +++ b/examples/controllers/rightClickExample.js @@ -0,0 +1,10 @@ +var MAPPING_NAME = "com.highfidelity.rightClickExample"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); +}); +Controller.enableMapping(MAPPING_NAME); + +Script.scriptEnding.connect(function () { + Controller.disableMapping(MAPPING_NAME); +}); \ No newline at end of file diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 1a0fae8158..3b256066f8 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -676,7 +676,7 @@ Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { Locker locker(_lock); - qCDebug(controllers) << "Attempting to enable mapping " << mappingName; + qCDebug(controllers) << "Attempting to " << (enable ? "enable" : "disable") << " mapping " << mappingName; auto iterator = _mappingsByName.find(mappingName); if (_mappingsByName.end() == iterator) { qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; From 46bedf7667db02fc0bdf74fd43efc1ff68eabd66 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 13:35:49 -0800 Subject: [PATCH 0623/1003] more tweaks to hydra mappings --- interface/resources/controllers/hydra.json | 14 ++++---------- interface/resources/controllers/standard.json | 6 +++--- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index 20d954932a..0cdeba1b8d 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -13,17 +13,11 @@ { "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.L1", "to": "Standard.LeftPrimaryThumb" }, + { "from": "Hydra.L2", "to": "Standard.LeftSecondaryThumb" }, - { "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" }, + { "from": "Hydra.R1", "to": "Standard.RightPrimaryThumb" }, + { "from": "Hydra.R2", "to": "Standard.RightSecondaryThumb" }, { "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 b08abcbaad..d312913a23 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -2,9 +2,8 @@ "name": "Standard to Action", "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, - { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.RX", + { "from": "Standard.LX", "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw", "filters": @@ -14,8 +13,9 @@ ] }, + { "from": "Standard.LX", "to": "Actions.Yaw" }, - { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RX", "to": "Actions.TranslateX" }, { "from": "Standard.RY", "filters": "invert", "to": "Actions.TranslateY" }, From d359aa3b654a6235da3e651bdfb05b5383be2982 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 13:41:16 -0800 Subject: [PATCH 0624/1003] more tweaks --- interface/resources/controllers/hydra.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/hydra.json b/interface/resources/controllers/hydra.json index 0cdeba1b8d..b517d1bad5 100644 --- a/interface/resources/controllers/hydra.json +++ b/interface/resources/controllers/hydra.json @@ -13,11 +13,12 @@ { "from": "Hydra.RB", "to": "Standard.RB" }, { "from": "Hydra.RS", "to": "Standard.RS" }, - { "from": "Hydra.L1", "to": "Standard.LeftPrimaryThumb" }, - { "from": "Hydra.L2", "to": "Standard.LeftSecondaryThumb" }, + { "from": [ "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" }, + { "from": [ "Hydra.L1", "Hydra.L2" ], "to": "Standard.LeftSecondaryThumb" }, + + { "from": [ "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" }, + { "from": [ "Hydra.R1", "Hydra.R2" ], "to": "Standard.RightSecondaryThumb" }, - { "from": "Hydra.R1", "to": "Standard.RightPrimaryThumb" }, - { "from": "Hydra.R2", "to": "Standard.RightSecondaryThumb" }, { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, { "from": "Hydra.RightHand", "to": "Standard.RightHand" } From 310a5c9bfbbea205fa5a86c7937c6c85e40aeaf9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 13:42:12 -0800 Subject: [PATCH 0625/1003] don't initialize SMI sdk unless we need it --- interface/src/devices/EyeTracker.cpp | 30 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index b7a0a8c299..2cfc2ecd75 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -118,18 +118,6 @@ void EyeTracker::init() { qCWarning(interfaceapp) << "Eye Tracker: Already initialized"; return; } - -#ifdef HAVE_IVIEWHMD - int result = smi_setCallback(eyeTrackerCallback); - if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); - QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); - } else { - _isInitialized = true; - } - - connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted())); -#endif } #ifdef HAVE_IVIEWHMD @@ -140,6 +128,10 @@ int EyeTracker::startStreaming(bool simulate) { #ifdef HAVE_IVIEWHMD void EyeTracker::onStreamStarted() { + if (!_isInitialized) { + return; + } + int result = _startStreamingWatcher.result(); _isStreaming = (result == SMI_RET_SUCCESS); @@ -171,6 +163,20 @@ void EyeTracker::onStreamStarted() { #endif void EyeTracker::setEnabled(bool enabled, bool simulate) { + if (enabled && !_isInitialized) { +#ifdef HAVE_IVIEWHMD + int result = smi_setCallback(eyeTrackerCallback); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); + QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + } else { + _isInitialized = true; + } + + connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted())); +#endif + } + if (!_isInitialized) { return; } From c2687b4998ad47c4b15dd6ba951e30786ea5562f Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 2 Nov 2015 14:17:07 -0800 Subject: [PATCH 0626/1003] Cleaning the feature not conditional do the true design --- .../src/controllers/UserInputMapper.cpp | 9 ++++----- .../impl/conditionals/EndpointConditional.h | 8 -------- .../impl/conditionals/NotConditional.cpp | 20 ++++++++++++------- .../impl/conditionals/NotConditional.h | 14 +++++++++++++ 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 5edc3390a6..a5d447d479 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -26,6 +26,7 @@ #include "Logging.h" #include "impl/conditionals/AndConditional.h" +#include "impl/conditionals/NotConditional.h" #include "impl/conditionals/EndpointConditional.h" #include "impl/conditionals/ScriptConditional.h" @@ -838,18 +839,16 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) auto input = findDeviceInput(conditionalToken); auto endpoint = endpointFor(input); - if (!endpoint) { - return Conditional::Pointer(); - } + auto conditional = std::make_shared(endpoint); if (!conditionalModifier.isEmpty()) { if (conditionalModifier == JSON_CONDITIONAL_MODIFIER_NOT) { - return std::make_shared(endpoint); + return std::make_shared(conditional); } } // Default and conditional behavior - return std::make_shared(endpoint); + return conditional; } return Conditional::parse(value); diff --git a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h index 36fd3e5daa..0ba1347087 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h @@ -23,13 +23,5 @@ private: Endpoint::Pointer _endpoint; }; -class NotEndpointConditional : public Conditional { -public: - NotEndpointConditional(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 index e30b060985..813c8ebfad 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp @@ -6,10 +6,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -// NOTE: we don't need to include this header unless/until we add additional symbols. -// By removing this header we prevent these warnings on windows: -// -// warning LNK4221: This object file does not define any previously undefined public symbols, -// so it will not be used by any link operation that consumes this library -// -//#include "NotConditional.h" + +#include "NotConditional.h" + +using namespace controller; + +bool NotConditional::satisfied() { + if (_operand) { + return (!_operand->satisfied()); + } else { + return false; + } +} + diff --git a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h index 3acda07106..6b19cf9505 100644 --- a/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h +++ b/libraries/controllers/src/controllers/impl/conditionals/NotConditional.h @@ -12,5 +12,19 @@ #include "../Conditional.h" +namespace controller { + + class NotConditional : public Conditional { + public: + using Pointer = std::shared_ptr; + + NotConditional(Conditional::Pointer operand) : _operand(operand) { } + + virtual bool satisfied() override; + + private: + Conditional::Pointer _operand; + }; +} #endif From 181901ed1930f11f25c2f3762ab43747501f838b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 14:42:28 -0800 Subject: [PATCH 0627/1003] distance grab works better with hmd on --- examples/controllers/handControllerGrab.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 9f7d579f10..b882777048 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -446,7 +446,7 @@ function MyController(hand) { this.currentObjectPosition = grabbedProperties.position; this.currentObjectRotation = grabbedProperties.rotation; this.currentObjectTime = now; - this.handPreviousPosition = handControllerPosition; + this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position); this.handPreviousRotation = handRotation; this.actionID = NULL_ACTION_ID; @@ -523,11 +523,10 @@ function MyController(hand) { this.currentAvatarOrientation = currentOrientation; // how far did hand move this timestep? - var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); - this.handPreviousPosition = handControllerPosition; + var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition); + this.handRelativePreviousPosition = handToAvatar; // magnify the hand movement but not the change from avatar movement & rotation - handMoved = Vec3.subtract(handMoved, avatarDeltaPosition); handMoved = Vec3.subtract(handMoved, handMovementFromTurning); var superHandMoved = Vec3.multiply(handMoved, radius); From 0211e00c86bc49a91c5d5944f8c87df4d8014752 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 2 Nov 2015 15:01:59 -0800 Subject: [PATCH 0628/1003] Cleaning the feature not conditional do the true design --- libraries/controllers/src/controllers/UserInputMapper.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index a5d447d479..192404b85a 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -839,6 +839,9 @@ Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) auto input = findDeviceInput(conditionalToken); auto endpoint = endpointFor(input); + if (!endpoint) { + return Conditional::Pointer(); + } auto conditional = std::make_shared(endpoint); if (!conditionalModifier.isEmpty()) { From f532607ccab1b94c150c6f207dbf49557c0148ad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 15:06:38 -0800 Subject: [PATCH 0629/1003] add touch and other gestures to standard --- .../src/controllers/StandardController.cpp | 22 +++++++++++++++++-- .../src/controllers/StandardControls.h | 17 ++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 31c023b1ea..5b68f38374 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -55,6 +55,9 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(LS, "LS"), makePair(RS, "RS"), + makePair(LS, "LS_TOUCH"), + makePair(RS, "RS_TOUCH"), + // Center buttons makePair(START, "Start"), makePair(BACK, "Back"), @@ -69,26 +72,41 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(LT, "LT"), makePair(RT, "RT"), - // Finger abstractions makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), + makePair(LEFT_THUMB_UP, "LeftThumbUp"), makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), + makePair(RIGHT_THUMB_UP, "RightThumbUp"), + + makePair(LEFT_PRIMARY_THUMB_TOUCH, "LeftPrimaryThumbTouch"), + makePair(LEFT_SECONDARY_THUMB_TOUCH, "LeftSecondaryThumbTouch"), + makePair(RIGHT_PRIMARY_THUMB_TOUCH, "RightPrimaryThumbTouch"), + makePair(RIGHT_SECONDARY_THUMB_TOUCH, "RightSecondaryThumbTouch"), + + makePair(LEFT_INDEX_POINT, "LeftIndexPoint"), + makePair(RIGHT_INDEX_POINT, "RightIndexPoint"), makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"), makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"), makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"), makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"), + makePair(LEFT_PRIMARY_INDEX_TOUCH, "LeftPrimaryIndexTouch"), + makePair(LEFT_SECONDARY_INDEX_TOUCH, "LeftSecondaryIndexTouch"), + makePair(RIGHT_PRIMARY_INDEX_TOUCH, "RightPrimaryIndexTouch"), + makePair(RIGHT_SECONDARY_INDEX_TOUCH, "RightSecondaryIndexTouch"), + makePair(LEFT_GRIP, "LeftGrip"), + makePair(LEFT_GRIP_TOUCH, "LeftGripTouch"), makePair(RIGHT_GRIP, "RightGrip"), + makePair(RIGHT_GRIP_TOUCH, "RightGripTouch"), // Poses makePair(LEFT_HAND, "LeftHand"), makePair(RIGHT_HAND, "RightHand"), - // Aliases, PlayStation style names makePair(LB, "L1"), makePair(RB, "R1"), diff --git a/libraries/controllers/src/controllers/StandardControls.h b/libraries/controllers/src/controllers/StandardControls.h index d5063f6034..bbd33c5cb3 100644 --- a/libraries/controllers/src/controllers/StandardControls.h +++ b/libraries/controllers/src/controllers/StandardControls.h @@ -39,16 +39,33 @@ namespace controller { // These don't map to SDL types LEFT_PRIMARY_THUMB, LEFT_SECONDARY_THUMB, + LEFT_PRIMARY_THUMB_TOUCH, + LEFT_SECONDARY_THUMB_TOUCH, + LS_TOUCH, + LEFT_THUMB_UP, + RIGHT_PRIMARY_THUMB, RIGHT_SECONDARY_THUMB, + RIGHT_PRIMARY_THUMB_TOUCH, + RIGHT_SECONDARY_THUMB_TOUCH, + RS_TOUCH, + RIGHT_THUMB_UP, LEFT_PRIMARY_INDEX, LEFT_SECONDARY_INDEX, + LEFT_PRIMARY_INDEX_TOUCH, + LEFT_SECONDARY_INDEX_TOUCH, + LEFT_INDEX_POINT, RIGHT_PRIMARY_INDEX, RIGHT_SECONDARY_INDEX, + RIGHT_PRIMARY_INDEX_TOUCH, + RIGHT_SECONDARY_INDEX_TOUCH, + RIGHT_INDEX_POINT, LEFT_GRIP, + LEFT_GRIP_TOUCH, RIGHT_GRIP, + RIGHT_GRIP_TOUCH, NUM_STANDARD_BUTTONS }; From f57f3c11386a8b474acd4cea790542f8e342625d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Mon, 2 Nov 2015 15:25:42 -0800 Subject: [PATCH 0630/1003] Disabled writing to depth buffer for particles and no longer sorting the, --- .../src/RenderableParticleEffectEntityItem.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index a7bdffc020..bc82159fbd 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -204,8 +204,6 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // sort particles back to front // NOTE: this is view frustum might be one frame out of date. auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); - ::zSortAxis = frustum->getDirection(); - qSort(particleDetails.begin(), particleDetails.end(), zSort); // allocate vertices _vertices.clear(); @@ -312,7 +310,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { if (!_untexturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setDepthTest(true, false, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); @@ -324,7 +322,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { if (!_texturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setDepthTest(true, false, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); From 02288e84e8017943f2507d4be87dc11985f39712 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 Nov 2015 15:35:30 -0800 Subject: [PATCH 0631/1003] CR feedback --- libraries/controllers/src/controllers/StandardController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 5b68f38374..fadbeee326 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -55,8 +55,8 @@ Input::NamedVector StandardController::getAvailableInputs() const { makePair(LS, "LS"), makePair(RS, "RS"), - makePair(LS, "LS_TOUCH"), - makePair(RS, "RS_TOUCH"), + makePair(LS_TOUCH, "LSTouch"), + makePair(RS_TOUCH, "RSTouch"), // Center buttons makePair(START, "Start"), From 15af28337b8494b6e0f8890431c8a8b0f1c545bf Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 2 Nov 2015 16:02:30 -0800 Subject: [PATCH 0632/1003] add color busters game for testing --- .../createColorBusterCubes.js | 89 ------------ .../createColorBusterWand.js | 55 -------- .../games/color_busters}/colorBusterWand.js | 101 +++++++++----- .../color_busters/createColorBusterCubes.js | 130 ++++++++++++++++++ .../color_busters/createColorBusterWand.js | 99 +++++++++++++ 5 files changed, 299 insertions(+), 175 deletions(-) delete mode 100644 examples/color_busters_game/createColorBusterCubes.js delete mode 100644 examples/color_busters_game/createColorBusterWand.js rename examples/{color_busters_game => example/games/color_busters}/colorBusterWand.js (69%) create mode 100644 examples/example/games/color_busters/createColorBusterCubes.js create mode 100644 examples/example/games/color_busters/createColorBusterWand.js diff --git a/examples/color_busters_game/createColorBusterCubes.js b/examples/color_busters_game/createColorBusterCubes.js deleted file mode 100644 index 2bc14e5a01..0000000000 --- a/examples/color_busters_game/createColorBusterCubes.js +++ /dev/null @@ -1,89 +0,0 @@ -var CUBE_DIMENSIONS = { - x: 1, - y: 1, - z: 1 -}; - -var NUMBER_OF_CUBES_PER_SIDE= 20; - -var STARTING_CORNER_POSITION = { - x: 0, - y: 0, - z: 0 -} - -var STARTING_COLORS = [ - ['red', { - red: 255, - green: 0, - blue: 0 - }], - ['yellow', { - red: 255, - green: 255, - blue: 0 - }], - ['blue', { - red: 0, - green: 0, - blue: 255 - }], - ['orange', { - red: 255, - green: 165, - blue: 0 - }], - ['violet', { - red: 128, - green: 0, - blue: 128 - }], - ['green', { - red: 0, - green: 255, - blue: 0 - }] -] - -function chooseStartingColor() { - var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)]; - return startingColor -} - -function createColorBusterCube(row, column, vertical) { - - var startingColor = chooseStartingColor(); - var colorBusterCubeProperties = { - name: 'Hifi-ColorBusterWand', - type: 'Model', - url: COLOR_WAND_MODEL_URL, - dimensions: COLOR_WAND_DIMENSIONS, - position: { - x: row, - y: vertical, - z: column - }, - userData: JSON.stringify({ - hifiColorBusterCubeKey: { - originalColorName: startingColor[0], - - } - }) - }; - - return Entities.addEntity(colorBusterCubeProperties); -} - -function createBoard() { - var vertical; - for (vertical = 0; vertical === NUMBER_OF_CUBES_PER_SIDE;vertical++) { - var row; - var column; - //create a single layer - for (row = 0; row === NUMBER_OF_CUBES_PER_SIDE; row++) { - for (column = 0; column === NUMBER_OF_CUBES_PER_SIDE; column++) { - this.createColorBusterCube(row, column, vertical) - } - } - } -} \ No newline at end of file diff --git a/examples/color_busters_game/createColorBusterWand.js b/examples/color_busters_game/createColorBusterWand.js deleted file mode 100644 index 4af5306177..0000000000 --- a/examples/color_busters_game/createColorBusterWand.js +++ /dev/null @@ -1,55 +0,0 @@ -var COLOR_WAND_MODEL_URL = ''; -var COLOR_WAND_DIMENSIONS = { - x: 0, - y: 0, - z: 0 -}; -var COLOR_WAND_START_POSITION = { - x: 0, - y: 0, - z: 0 -}; -var STARTING_COLORS = [ - ['red', { - red: 255, - green: 0, - blue: 0 - }], - ['yellow', { - red: 255, - green: 255, - blue: 0 - }], - ['blue', { - red: 0, - green: 0, - blue: 255 - }] -] - -function chooseStartingColor() { - var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)]; - return startingColor -} - -function createColorBusterWand() { - - var startingColor = chooseStartingColor(); - var colorBusterWandProperties = { - name: 'Hifi-ColorBusterWand', - type: 'Model', - url: COLOR_WAND_MODEL_URL, - dimensions: COLOR_WAND_DIMENSIONS, - position: COLOR_WAND_START_POSITION, - userData: JSON.stringify({ - hifiColorBusterWandKey: { - owner: MyAvatar.sessionUUID, - currentColor: startingColor[1], - originalColorName: startingColor[0], - colorLocked: false - } - }) - }; - - Entities.addEntity(colorBusterWandProperties); -} \ No newline at end of file diff --git a/examples/color_busters_game/colorBusterWand.js b/examples/example/games/color_busters/colorBusterWand.js similarity index 69% rename from examples/color_busters_game/colorBusterWand.js rename to examples/example/games/color_busters/colorBusterWand.js index 26948da91b..6080d6f345 100644 --- a/examples/color_busters_game/colorBusterWand.js +++ b/examples/example/games/color_busters/colorBusterWand.js @@ -1,42 +1,63 @@ +// +// colorBusterWand.js +// +// Created by James B. Pollack @imgntn on 11/2/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This is the entity script that attaches to a wand for the Color Busters game +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + + (function() { + Script.include("../../../libraries/utils.js"); - //seconds that the combination lasts - var COMBINED_COLOR_DURATION = 10; + var COMBINED_COLOR_DURATION = 5; - var INDICATOR_OFFSET_UP = 0.5; + var INDICATOR_OFFSET_UP = 0.40; - var REMOVE_CUBE_SOUND_URL = ''; - var COMBINE_COLORS_SOUND_URL = ''; + var REMOVE_CUBE_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/color_busters/boop.wav'; + var COMBINE_COLORS_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/color_busters/powerup.wav'; + + var COLOR_INDICATOR_DIMENSIONS = { + x: 0.10, + y: 0.10, + z: 0.10 + }; var _this; - function ColorWand() { + function ColorBusterWand() { _this = this; - }; + } ColorBusterWand.prototype = { combinedColorsTimer: null, - + soundIsPlaying: false, preload: function(entityID) { print("preload"); this.entityID = entityID; this.REMOVE_CUBE_SOUND = SoundCache.getSound(REMOVE_CUBE_SOUND_URL); this.COMBINE_COLORS_SOUND = SoundCache.getSound(COMBINE_COLORS_SOUND_URL); - }, - entityCollisionWithEntity: function(me, otherEntity, collision) { - + collisionWithEntity: function(me, otherEntity, collision) { var otherProperties = Entities.getEntityProperties(otherEntity, ["name", "userData"]); - var myProperties = Entities.getEntityProperties(otherEntity, ["userData"]); + var myProperties = Entities.getEntityProperties(me, ["userData"]); var myUserData = JSON.parse(myProperties.userData); var otherUserData = JSON.parse(otherProperties.userData); if (otherProperties.name === 'Hifi-ColorBusterWand') { + print('HIT ANOTHER COLOR WAND!!'); if (otherUserData.hifiColorBusterWandKey.colorLocked !== true && myUserData.hifiColorBusterWandKey.colorLocked !== true) { if (otherUserData.hifiColorBusterWandKey.originalColorName === myUserData.hifiColorBusterWandKey.originalColorName) { + print('BUT ITS THE SAME COLOR!') return; } else { + print('COMBINE COLORS!' + this.entityID); this.combineColorsWithOtherWand(otherUserData.hifiColorBusterWandKey.originalColorName, myUserData.hifiColorBusterWandKey.originalColorName); } } @@ -44,15 +65,23 @@ if (otherProperties.name === 'Hifi-ColorBusterCube') { if (otherUserData.hifiColorBusterCubeKey.originalColorName === myUserData.hifiColorBusterWandKey.currentColor) { - removeCubeOfSameColor(otherEntity); + print('HIT THE SAME COLOR CUBE'); + this.removeCubeOfSameColor(otherEntity); + } else { + print('HIT A CUBE OF A DIFFERENT COLOR'); } - } }, combineColorsWithOtherWand: function(otherColor, myColor) { - var newColor; + print('combining my :' + myColor + " with their: " + otherColor); + if ((myColor === 'violet') || (myColor === 'orange') || (myColor === 'green')) { + print('MY WAND ALREADY COMBINED'); + return; + } + + var newColor; if ((otherColor === 'red' && myColor == 'yellow') || (myColor === 'red' && otherColor === 'yellow')) { //orange newColor = 'orange'; @@ -73,19 +102,18 @@ _this.combinedColorsTimer = null; }, COMBINED_COLOR_DURATION * 1000); - setEntityCustomData(hifiColorWandKey, this.entityID, { + setEntityCustomData('hifiColorBusterWandKey', this.entityID, { owner: MyAvatar.sessionUUID, - currentColor: newColor + currentColor: newColor, originalColorName: myColor, colorLocked: false }); - _this.setCurrentColor(newColor); + this.playSoundAtCurrentPosition(false); }, setCurrentColor: function(newColor) { - var color; if (newColor === 'orange') { @@ -138,23 +166,27 @@ Entities.editEntity(this.colorIndicator, { color: color - }) + }); + + // print('SET THIS COLOR INDICATOR TO:' + newColor); }, resetToOriginalColor: function(myColor) { - setEntityCustomData(hifiColorWandKey, this.entityID, { + setEntityCustomData('hifiColorBusterWandKey', this.entityID, { owner: MyAvatar.sessionUUID, - currentColor: myColor + currentColor: myColor, originalColorName: myColor, - colorLocked: true + colorLocked: false }); this.setCurrentColor(myColor); }, removeCubeOfSameColor: function(cube) { + this.playSoundAtCurrentPosition(true); Entities.callEntityMethod(cube, 'cubeEnding'); Entities.deleteEntity(cube); + }, startNearGrab: function() { @@ -164,24 +196,31 @@ continueNearGrab: function() { this.currentProperties = Entities.getEntityProperties(this.entityID); + + var color = JSON.parse(this.currentProperties.userData).hifiColorBusterWandKey.currentColor; + + this.setCurrentColor(color); this.updateColorIndicatorLocation(); }, releaseGrab: function() { - this.deleteEntity(this.colorIndicator); + Entities.deleteEntity(this.colorIndicator); if (this.combinedColorsTimer !== null) { Script.clearTimeout(this.combinedColorsTimer); } }, - createColorIndicator: function() { + createColorIndicator: function(color) { + + var properties = { name: 'Hifi-ColorBusterIndicator', type: 'Box', dimensions: COLOR_INDICATOR_DIMENSIONS, - color: this.currentProperties.position, - position: this.currentProperties.position + position: this.currentProperties.position, + collisionsWillMove: false, + ignoreForCollisions: true } this.colorIndicator = Entities.addEntity(properties); @@ -204,15 +243,15 @@ }, - playSoundAtCurrentPosition: function(removeCubeSound) { - var position = Entities.getEntityProperties(this.entityID, "position").position; + playSoundAtCurrentPosition: function(isRemoveCubeSound) { + var position = Entities.getEntityProperties(this.entityID, "position").position; var audioProperties = { - volume: 0.25, + volume: 0.5, position: position }; - if (removeCubeSound) { + if (isRemoveCubeSound === true) { Audio.playSound(this.REMOVE_CUBE_SOUND, audioProperties); } else { Audio.playSound(this.COMBINE_COLORS_SOUND, audioProperties); diff --git a/examples/example/games/color_busters/createColorBusterCubes.js b/examples/example/games/color_busters/createColorBusterCubes.js new file mode 100644 index 0000000000..1b7aac51a5 --- /dev/null +++ b/examples/example/games/color_busters/createColorBusterCubes.js @@ -0,0 +1,130 @@ +// +// createColorBusterCubes.js +// +// Created by James B. Pollack @imgntn on 11/2/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This script creates cubes that can be removed with a Color Buster wand. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +var DELETE_AT_ENDING = true; + +var CUBE_DIMENSIONS = { + x: 1, + y: 1, + z: 1 +}; + +var NUMBER_OF_CUBES_PER_SIDE = 5; + +var STARTING_CORNER_POSITION = { + x: 100, + y: 100, + z: 100 +}; +var STARTING_COLORS = [ + ['red', { + red: 255, + green: 0, + blue: 0 + }], + ['yellow', { + red: 255, + green: 255, + blue: 0 + }], + ['blue', { + red: 0, + green: 0, + blue: 255 + }], + ['orange', { + red: 255, + green: 165, + blue: 0 + }], + ['violet', { + red: 128, + green: 0, + blue: 128 + }], + ['green', { + red: 0, + green: 255, + blue: 0 + }] +]; + +function chooseStartingColor() { + var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)]; + return startingColor; +} + +var cubes = []; + +function createColorBusterCube(row, column, vertical) { + + print('make cube at ' + row + ':' + column + ":" + vertical); + + var position = { + x: STARTING_CORNER_POSITION.x + row, + y: STARTING_CORNER_POSITION.y + vertical, + z: STARTING_CORNER_POSITION.z + column + }; + + var startingColor = chooseStartingColor(); + var colorBusterCubeProperties = { + name: 'Hifi-ColorBusterCube', + type: 'Box', + dimensions: CUBE_DIMENSIONS, + collisionsWillMove: false, + ignoreForCollisions: false, + color: startingColor[1], + position: position, + userData: JSON.stringify({ + hifiColorBusterCubeKey: { + originalColorName: startingColor[0] + }, + grabbableKey: { + grabbable: false + } + }) + }; + var cube = Entities.addEntity(colorBusterCubeProperties); + cubes.push(cube); + return cube +} + +function createBoard() { + var vertical; + var row; + var column; + for (vertical = 0; vertical < NUMBER_OF_CUBES_PER_SIDE; vertical++) { + print('vertical:' + vertical) + //create a single layer + for (row = 0; row < NUMBER_OF_CUBES_PER_SIDE; row++) { + print('row:' + row) + for (column = 0; column < NUMBER_OF_CUBES_PER_SIDE; column++) { + print('column:' + column) + createColorBusterCube(row, column, vertical) + } + } + } +} + +function deleteCubes() { + while (cubes.length > 0) { + Entities.deleteEntity(cubes.pop()); + } +} + +if (DELETE_AT_ENDING === true) { + Script.scriptEnding.connect(deleteCubes); + +} + +createBoard(); \ No newline at end of file diff --git a/examples/example/games/color_busters/createColorBusterWand.js b/examples/example/games/color_busters/createColorBusterWand.js new file mode 100644 index 0000000000..a08f529aa8 --- /dev/null +++ b/examples/example/games/color_busters/createColorBusterWand.js @@ -0,0 +1,99 @@ +// +// createColorBusterWand.js +// +// Created by James B. Pollack @imgntn on 11/2/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This script creates a wand that can be used to remove color buster blocks. Touch your wand to someone else's to combine colors. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var DELETE_AT_ENDING = false; + +var COLOR_WAND_MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/color_busters/wand.fbx'; +var COLOR_WAND_COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/color_busters/wand_collision_hull.obj'; +var COLOR_WAND_SCRIPT_URL = Script.resolvePath('colorBusterWand.js'); + +var COLOR_WAND_DIMENSIONS = { + x: 0.04, + y: 0.87, + z: 0.04 +}; + +var COLOR_WAND_START_POSITION = { + x: 0, + y: 0, + z: 0 +}; + +var STARTING_COLORS = [ + ['red', { + red: 255, + green: 0, + blue: 0 + }], + ['yellow', { + red: 255, + green: 255, + blue: 0 + }], + ['blue', { + red: 0, + green: 0, + blue: 255 + }] +]; + +var center = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: 0.5, + z: 0 +}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); + + +function chooseStartingColor() { + var startingColor = STARTING_COLORS[Math.floor(Math.random() * STARTING_COLORS.length)]; + return startingColor +} + +var wand; + +function createColorBusterWand() { + var startingColor = chooseStartingColor(); + var colorBusterWandProperties = { + name: 'Hifi-ColorBusterWand', + type: 'Model', + modelURL: COLOR_WAND_MODEL_URL, + shapeType: 'compound', + compoundShapeURL: COLOR_WAND_COLLISION_HULL_URL, + dimensions: COLOR_WAND_DIMENSIONS, + position: center, + script: COLOR_WAND_SCRIPT_URL, + collisionsWillMove: true, + userData: JSON.stringify({ + hifiColorBusterWandKey: { + owner: MyAvatar.sessionUUID, + currentColor: startingColor[0], + originalColorName: startingColor[0], + colorLocked: false + }, + grabbableKey: { + invertSolidWhileHeld: false + } + }) + }; + + wand = Entities.addEntity(colorBusterWandProperties); +} + +function deleteWand() { + Entities.deleteEntity(wand); +} + +if (DELETE_AT_ENDING === true) { + Script.scriptEnding.connect(deleteWand); +} + +createColorBusterWand(); \ No newline at end of file From b77945d430d4bd09e8dff43e6bef56a3d7a6cc47 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Mon, 2 Nov 2015 15:50:27 -0800 Subject: [PATCH 0633/1003] Vive - fix input drifting Conflicts: libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp --- interface/resources/controllers/vive.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/vive.json b/interface/resources/controllers/vive.json index 6a3c562e44..c46bcd9402 100644 --- a/interface/resources/controllers/vive.json +++ b/interface/resources/controllers/vive.json @@ -1,15 +1,15 @@ { "name": "Vive to Standard", "channels": [ - { "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.LY", "when": "Vive.LS", "filters": "invert", "to": "Standard.LY" }, + { "from": "Vive.LX", "when": "Vive.LS", "to": "Standard.LX" }, { "from": "Vive.LT", "to": "Standard.LT" }, { "from": "Vive.LB", "to": "Standard.LB" }, { "from": "Vive.LS", "to": "Standard.LS" }, - { "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" }, - { "from": "Vive.RX", "to": "Standard.RX" }, + { "from": "Vive.RY", "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" }, + { "from": "Vive.RX", "when": "Vive.RS", "to": "Standard.RX" }, { "from": "Vive.RT", "to": "Standard.RT" }, { "from": "Vive.RB", "to": "Standard.RB" }, From 0a5f43bba78732ad3d31bc031bcf21c5fde4dbad Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 16:16:13 -0800 Subject: [PATCH 0634/1003] fix up boolean test --- examples/controllers/handControllerGrab.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b882777048..74c830616a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -18,7 +18,7 @@ Script.include("../libraries/utils.js"); // // add lines where the hand ray picking is happening // -var WANT_DEBUG = false; +var WANT_DEBUG = true; // // these tune time-averaging and "on" value for analog trigger @@ -352,14 +352,14 @@ function MyController(hand) { var intersection = Entities.findRayIntersection(pickRayBacked, true); - if (intersection.intersects && intersection.properties.locked === 0) { + if (intersection.intersects && !intersection.properties.locked) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; //this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - if (grabbableData["turnOffOppositeBeam"] === true) { + if (grabbableData["turnOffOppositeBeam"]) { if (this.hand === RIGHT_HAND) { disabledHand = LEFT_HAND; } else { @@ -369,7 +369,7 @@ function MyController(hand) { disabledHand = 'none'; } - if (grabbableData.grabbable === false) { + if (!grabbableData.grabbable) { this.grabbedEntity = null; continue; } @@ -391,7 +391,7 @@ function MyController(hand) { this.grabbedEntity = null; } else { // the hand is far from the intersected object. go into distance-holding mode - if (intersection.properties.collisionsWillMove === 1) { + if (intersection.properties.collisionsWillMove) { this.setState(STATE_DISTANCE_HOLDING); } else { this.setState(STATE_FAR_GRABBING_NON_COLLIDING); @@ -409,7 +409,7 @@ function MyController(hand) { for (i = 0; i < nearbyEntities.length; i++) { var grabbableDataForCandidate = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (grabbableDataForCandidate.grabbable === false) { + if (!grabbableDataForCandidate.grabbable) { continue; } var propsForCandidate = @@ -427,7 +427,7 @@ function MyController(hand) { } if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } else if (props.locked === 0) { + } else if (!props.locked) { this.setState(STATE_NEAR_GRABBING); } } @@ -569,7 +569,7 @@ function MyController(hand) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); var turnOffOtherHand = grabbableData["turnOffOtherHand"]; - if (turnOffOtherHand === true) { + if (turnOffOtherHand) { //don't activate the second hand grab because the script is handling the second hand logic return; } @@ -782,11 +782,11 @@ function MyController(hand) { // we haven't been touched before, but either right or left is touching us now _this.allTouchedIDs[id] = true; _this.startTouch(id); - } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id] === true) { + } else if ((leftIsTouching || rightIsTouching) && _this.allTouchedIDs[id]) { // we have been touched before and are still being touched // continue touch _this.continueTouch(id); - } else if (_this.allTouchedIDs[id] === true) { + } else if (_this.allTouchedIDs[id]) { delete _this.allTouchedIDs[id]; _this.stopTouch(id); From fe2c0effd04099c4c6cd256cb74af6b40f0667ef Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 Nov 2015 16:17:46 -0800 Subject: [PATCH 0635/1003] oops left debug on --- 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 74c830616a..30c99f43f8 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -18,7 +18,7 @@ Script.include("../libraries/utils.js"); // // add lines where the hand ray picking is happening // -var WANT_DEBUG = true; +var WANT_DEBUG = false; // // these tune time-averaging and "on" value for analog trigger From aeac31cf6a5470f760af1536adb718e782b3d4ec Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 2 Nov 2015 16:37:24 -0800 Subject: [PATCH 0636/1003] dont delete cubes by default --- examples/example/games/color_busters/createColorBusterCubes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example/games/color_busters/createColorBusterCubes.js b/examples/example/games/color_busters/createColorBusterCubes.js index 1b7aac51a5..6a2942bbe9 100644 --- a/examples/example/games/color_busters/createColorBusterCubes.js +++ b/examples/example/games/color_busters/createColorBusterCubes.js @@ -11,7 +11,7 @@ // -var DELETE_AT_ENDING = true; +var DELETE_AT_ENDING = false; var CUBE_DIMENSIONS = { x: 1, From 0c12bb5a4ea613697599d77ad3970cdb0390596f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 2 Nov 2015 18:47:18 -0800 Subject: [PATCH 0637/1003] Remove idleTimer and call idle directly from paintGL. --- interface/src/Application.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d41679bea..4c55e05b01 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -160,7 +160,6 @@ static QTimer balanceUpdateTimer; static QTimer identityPacketTimer; static QTimer billboardPacketTimer; static QTimer checkFPStimer; -static QTimer idleTimer; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SVO_EXTENSION = ".svo"; @@ -844,7 +843,6 @@ void Application::cleanupBeforeQuit() { identityPacketTimer.stop(); billboardPacketTimer.stop(); checkFPStimer.stop(); - idleTimer.stop(); QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); // save state @@ -982,9 +980,6 @@ void Application::initializeGL() { connect(&checkFPStimer, &QTimer::timeout, this, &Application::checkFPS); checkFPStimer.start(1000); - // call our idle function whenever we can - connect(&idleTimer, &QTimer::timeout, this, &Application::idle); - idleTimer.start(TARGET_SIM_FRAME_PERIOD_MS); _idleLoopStdev.reset(); // update before the first render @@ -1048,6 +1043,9 @@ void Application::initializeUi() { } void Application::paintGL() { + + idle(); + PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("paintGL"); From 94ae1ef638c5e48788853fe1a6ceb13315214999 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 2 Nov 2015 20:37:57 -0800 Subject: [PATCH 0638/1003] Fix accumulateTime --- libraries/animation/src/AnimClip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 8f50874ed3..b2b6f728fd 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -81,7 +81,7 @@ void AnimClip::setCurrentFrameInternal(float frame) { // because dt is 0, we should not encounter any triggers const float dt = 0.0f; Triggers triggers; - _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame, dt, _loopFlag, _id, triggers); + _frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers); } void AnimClip::copyFromNetworkAnim() { From b74ed36883ad03602096c8280a230e5475df8b51 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 2 Nov 2015 20:39:31 -0800 Subject: [PATCH 0639/1003] Fix outro start times, and introduce away states (which are not used in C++ code, but are used in script). --- .../defaultAvatar_full/avatar-animation.json | 89 ++++++++++++++++--- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index fd1099a85b..9398b94890 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -178,7 +178,7 @@ "type": "clip", "data": { "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", - "startFrame": 0.0, + "startFrame": 12.0, "endFrame": 65.0, "timeScale": 1.0, "loopFlag": false @@ -327,7 +327,7 @@ "type": "clip", "data": { "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", - "startFrame": 0.0, + "startFrame": 12.0, "endFrame": 65.0, "timeScale": 1.0, "loopFlag": false @@ -378,15 +378,16 @@ "states": [ { "id": "idle", - "interpTarget": 6, - "interpDuration": 6, + "interpTarget": 15, + "interpDuration": 15, "transitions": [ { "var": "isMovingForward", "state": "walkFwd" }, { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" } + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } ] }, { @@ -399,7 +400,8 @@ { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" } + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } ] }, { @@ -412,7 +414,8 @@ { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" } + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } ] }, { @@ -425,7 +428,8 @@ { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" } + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } ] }, { @@ -438,7 +442,8 @@ { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" } + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } ] }, { @@ -451,7 +456,8 @@ { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningLeft", "state": "turnLeft" } + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } ] }, { @@ -464,7 +470,32 @@ { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningRight", "state": "turnRight" } + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "awayIntro", + "interpTarget": 30, + "interpDuration": 30, + "transitions": [ + { "var": "awayIntroOnDone", "state": "away"} + ] + }, + { + "id": "away", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isNotAway", "state": "awayOutro" } + ] + }, + { + "id": "awayOutro", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "awayOutroOnDone", "state": "idle" } ] } ] @@ -704,6 +735,42 @@ "children": [] } ] + }, + { + "id": "awayIntro", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "startFrame": 0.0, + "endFrame": 83.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "away", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "startFrame": 83.0, + "endFrame": 84.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "awayOutro", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "startFrame": 84.0, + "endFrame": 167.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] } ] } From 1b7ed3d847fa71d6df316bf36755b43daedf4c20 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 2 Nov 2015 20:40:40 -0800 Subject: [PATCH 0640/1003] away script --- examples/away.js | 84 ++++++++++++++++++++++++++++++++++++++ examples/defaultScripts.js | 1 + 2 files changed, 85 insertions(+) create mode 100644 examples/away.js diff --git a/examples/away.js b/examples/away.js new file mode 100644 index 0000000000..9fd688dbb7 --- /dev/null +++ b/examples/away.js @@ -0,0 +1,84 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*global HMD, AudioDevice, MyAvatar, Controller, Script, Overlays, print*/ + +// Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. +// See MAIN CONTROL, below, for what "paused" actually does. + +var OVERLAY_DATA = { + text: "Paused:\npress any key to continue", + font: {size: 75}, + color: {red: 200, green: 255, blue: 255}, + alpha: 0.9 +}; + +// ANIMATION +// We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect +// using an animation graph with a state that we turn on and off through the animation var defined with that state. +var awayAnimationHandlerId, activeAnimationHandlerId; +function playAwayAnimation() { + function animateAway() { + return {isAway: true, isNotAway: false, isNotMoving: false}; + } + awayAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateAway, null); +} +function stopAwayAnimation() { + MyAvatar.removeAnimationStateHandler(awayAnimationHandlerId); + function animateActive(state) { + if (!state.isAway) { // Once the right state gets reflected back to us, we don't need the hander any more. + // But we are locked against handler changes during the execution of a handler, so remove asynchronously. + Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0); + } + return {isAway: false, isNotAway: true}; // IWBNI we had a way of deleting an anim var. + } + activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['isAway']); +} + +// OVERLAY +var overlay = Overlays.addOverlay("text", OVERLAY_DATA); +function showOverlay() { + var screen = Controller.getViewportDimensions(); + Overlays.editOverlay(overlay, {visible: true, x: screen.x / 4, y: screen.y / 4}); +} +function hideOverlay() { + Overlays.editOverlay(overlay, {visible: false}); +} +hideOverlay(); + + +// MAIN CONTROL +var wasMuted, isAway; +function goAway() { + if (isAway) { + return; + } + isAway = true; + print('going "away"'); + wasMuted = AudioDevice.getMuted(); + if (!wasMuted) { + AudioDevice.toggleMute(); + } + MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view + playAwayAnimation(); // animation is still seen by others + showOverlay(); +} +function goActive(event) { + if (!isAway) { + if (event.text === '.') { + goAway(); + } + return; + } + isAway = false; + print('going "active"'); + if (!wasMuted) { + AudioDevice.toggleMute(); + } + MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. + stopAwayAnimation(); + hideOverlay(); +} +Controller.keyPressEvent.connect(goActive); +if (HMD.active) { + goAway(); +} diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 53e7e43aa9..6efe6edef3 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -8,6 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.load("away.js"); Script.load("progress.js"); Script.load("edit.js"); Script.load("selectAudioDevice.js"); From 764ecba2a8bb337f0131a42e0de1f70a756a1db8 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 21:55:35 -0800 Subject: [PATCH 0641/1003] Adding diagnostics to track down the 'black rectangles of death' --- interface/src/avatar/Avatar.cpp | 29 ++++++++++++++++++++++++----- libraries/gpu/src/gpu/Batch.cpp | 22 ++++++++++++++++++++++ libraries/gpu/src/gpu/Batch.h | 28 ++++++++++++++++++++++++++++ libraries/gpu/src/gpu/GLBackend.cpp | 22 ++++++++++++++++++++++ libraries/gpu/src/gpu/GLBackend.h | 3 +++ 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ae3aec3572..675a71a2f4 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -73,6 +73,7 @@ namespace render { avatarPtr->setDisplayingLookatTarget(renderLookAtTarget); if (avatarPtr->isInitialized() && args) { + PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload"); avatarPtr->render(args, qApp->getCamera()->getPosition()); } } @@ -334,6 +335,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } auto& batch = *renderArgs->_batch; + PROFILE_RANGE_BATCH(batch, __FUNCTION__); if (glm::distance(DependencyManager::get()->getMyAvatar()->getPosition(), _position) < 10.0f) { auto geometryCache = DependencyManager::get(); @@ -360,6 +362,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } if (havePosition && haveRotation) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":leftHandPointer"); Transform pointerTransform; pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); @@ -383,6 +386,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } if (havePosition && haveRotation) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":rightHandPointer"); Transform pointerTransform; pointerTransform.setTranslation(position); pointerTransform.setRotation(rotation); @@ -455,6 +459,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } @@ -464,6 +469,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { static const float INDICATOR_RADIUS = 0.03f; static const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + INDICATOR_OFFSET, _position.z); + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderFocusIndicator"); Transform transform; transform.setTranslation(position); transform.postScale(INDICATOR_RADIUS); @@ -472,6 +478,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { // If the avatar is looking at me, indicate that they are if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderLookingAtMe"); const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; const float LOOKING_AT_ME_ALPHA_START = 0.8f; const float LOOKING_AT_ME_DURATION = 0.5f; // seconds @@ -517,6 +524,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { const float MIN_VOICE_SPHERE_DISTANCE = 12.0f; if (Menu::getInstance()->isOptionChecked(MenuOption::BlueSpeechSphere) && distanceToTarget > MIN_VOICE_SPHERE_DISTANCE) { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderVoiceSphere"); // render voice intensity sphere for avatars that are farther away const float MAX_SPHERE_ANGLE = 10.0f * RADIANS_PER_DEGREE; @@ -684,6 +692,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) { glm::vec2 texCoordBottomRight(1.0f, 1.0f); gpu::Batch& batch = *renderArgs->_batch; + PROFILE_RANGE_BATCH(batch, __FUNCTION__); batch.setResourceTexture(0, _billboardTexture->getGPUTexture()); DependencyManager::get()->bindSimpleProgram(batch, true); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, @@ -766,6 +775,8 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, cons } void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, const glm::vec3& textPosition) const { + PROFILE_RANGE_BATCH(batch, __FUNCTION__); + bool shouldShowReceiveStats = DependencyManager::get()->shouldShowReceiveStats() && !isMyAvatar(); // If we have nothing to draw, or it's totally transparent, or it's too close or behind the camera, return @@ -816,17 +827,24 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co // Test on extent above insures abs(height) > 0.0f textTransform.postScale(1.0f / height); batch.setModelTransform(textTransform); - - DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true); - DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, - bevelDistance, backgroundColor); + + { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); + DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true); + DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, + bevelDistance, backgroundColor); + } + // Render actual name QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); // Render text slightly in front to avoid z-fighting textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize())); batch.setModelTransform(textTransform); - renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); + { + PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); + renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor); + } } } @@ -1089,6 +1107,7 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g points << p1a << p1b << p2a << p1b << p2a << p2b; } + PROFILE_RANGE_BATCH(batch, __FUNCTION__); // TODO: this is really inefficient constantly recreating these vertices buffers. It would be // better if the avatars cached these buffers for each of the joints they are rendering geometryCache->updateVertices(_jointConesID, points, color); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index c3e186a630..2e3ea7fc36 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -22,6 +22,14 @@ ProfileRange::ProfileRange(const char *name) { ProfileRange::~ProfileRange() { nvtxRangePop(); } + +ProfileRangeBatch::ProfileRangeBatch(gpu::Batch& batch, const char *name) : _batch(batch) { + _batch.pushProfileRange(name); +} + +ProfileRangeBatch::~ProfileRangeBatch() { + _batch.popProfileRange(); +} #endif #define ADD_COMMAND(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); @@ -391,3 +399,17 @@ QDebug& operator<<(QDebug& debug, const Batch::CacheState& cacheState) { return debug; } + +// Debugging +void Batch::pushProfileRange(const char* name) { +#if defined(NSIGHT_FOUND) + ADD_COMMAND(pushProfileRange); + _params.push_back(_profileRanges.cache(name)); +#endif +} + +void Batch::popProfileRange() { +#if defined(NSIGHT_FOUND) + ADD_COMMAND(popProfileRange); +#endif +} diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index deb70f7a68..8397f92da6 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -229,6 +229,10 @@ public: // Reset the stage caches and states void resetStages(); + // Debugging + void pushProfileRange(const char* name); + void popProfileRange(); + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API @@ -324,6 +328,9 @@ public: COMMAND_glColor4f, + COMMAND_pushProfileRange, + COMMAND_popProfileRange, + NUM_COMMANDS, }; typedef std::vector Commands; @@ -389,6 +396,7 @@ public: typedef Cache::Vector PipelineCaches; typedef Cache::Vector FramebufferCaches; typedef Cache::Vector QueryCaches; + typedef Cache::Vector ProfileRangeCaches; typedef Cache>::Vector LambdaCache; // Cache Data in a byte array if too big to fit in Param @@ -416,6 +424,7 @@ public: FramebufferCaches _framebuffers; QueryCaches _queries; LambdaCache _lambdas; + ProfileRangeCaches _profileRanges; NamedBatchDataMap _namedData; @@ -429,6 +438,25 @@ protected: } +#if defined(NSIGHT_FOUND) + +class ProfileRangeBatch { +public: + ProfileRangeBatch(gpu::Batch& batch, const char *name); + ~ProfileRangeBatch(); + +private: + gpu::Batch& _batch; +}; + +#define PROFILE_RANGE_BATCH(batch, name) ProfileRangeBatch profileRangeThis(batch, name); + +#else + +#define PROFILE_RANGE_BATCH(batch, name) + +#endif + QDebug& operator<<(QDebug& debug, const gpu::Batch::CacheState& cacheState); #endif diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 19edbaee5f..e49a3ba6c0 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -15,6 +15,11 @@ #include #include +#if defined(NSIGHT_FOUND) +#include "nvToolsExt.h" +#endif + + using namespace gpu; GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = @@ -69,6 +74,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUniformMatrix4fv), (&::gpu::GLBackend::do_glColor4f), + + (&::gpu::GLBackend::do_pushProfileRange), + (&::gpu::GLBackend::do_popProfileRange), }; void GLBackend::init() { @@ -710,3 +718,17 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) { } (void) CHECK_GL_ERROR(); } + + +void GLBackend::do_pushProfileRange(Batch& batch, uint32 paramOffset) { +#if defined(NSIGHT_FOUND) + auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + nvtxRangePush(name.c_str()); +#endif +} + +void GLBackend::do_popProfileRange(Batch& batch, uint32 paramOffset) { +#if defined(NSIGHT_FOUND) + nvtxRangePop(); +#endif +} diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 6c359d81f1..9f1e17205c 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -479,6 +479,9 @@ protected: void do_glColor4f(Batch& batch, uint32 paramOffset); + void do_pushProfileRange(Batch& batch, uint32 paramOffset); + void do_popProfileRange(Batch& batch, uint32 paramOffset); + typedef void (GLBackend::*CommandCall)(Batch&, uint32); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; }; From 603405ad61d8009fa81e0c6c2e791dc2d3731af4 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Mon, 2 Nov 2015 23:29:15 -0800 Subject: [PATCH 0642/1003] Do not merge - Spacemouse --- interface/src/Application.cpp | 13 +- interface/src/Menu.cpp | 12 +- interface/src/devices/3DConnexionClient.cpp | 972 -------------------- interface/src/devices/3DConnexionClient.h | 223 ----- 4 files changed, 10 insertions(+), 1210 deletions(-) delete mode 100755 interface/src/devices/3DConnexionClient.cpp delete mode 100755 interface/src/devices/3DConnexionClient.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d41679bea..dc166e729b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -100,7 +100,7 @@ #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "CrashHandler.h" -#include "devices/3DConnexionClient.h" +#include "input-plugins/SpacemouseManager.h" #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" #include "devices/Faceshift.h" @@ -729,10 +729,8 @@ 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 + SpacemouseManager::getInstance().init(); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); @@ -1839,9 +1837,10 @@ void Application::focusOutEvent(QFocusEvent* event) { inputPlugin->pluginFocusOutEvent(); } } -#if 0 - ConnexionData::getInstance().focusOutEvent(); -#endif + + //SpacemouseDevice::getInstance().focusOutEvent(); + //SpacemouseManager::getInstance().getDevice()->focusOutEvent(); + SpacemouseManager::getInstance().ManagerFocusOutEvent(); // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1565db2905..c15ce4a066 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -30,7 +30,7 @@ #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" #include "devices/RealSense.h" -#include "devices/3DConnexionClient.h" +#include "input-plugins/SpacemouseManager.h" #include "MainWindow.h" #include "scripting/MenuScriptingInterface.h" #include "ui/AssetUploadDialogFactory.h" @@ -464,13 +464,9 @@ 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::Connexion, 0, false, &SpacemouseManager::getInstance(), SLOT(toggleConnexion(bool))); + 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 deleted file mode 100755 index 38a9b4cb29..0000000000 --- a/interface/src/devices/3DConnexionClient.cpp +++ /dev/null @@ -1,972 +0,0 @@ -// -// 3DConnexionClient.cpp -// interface/src/devices -// -// Created by MarcelEdward Verhagen on 09-06-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 "3DConnexionClient.h" - -#if 0 -#include -#include - -#include "Menu.h" - -const float MAX_AXIS = 75.0f; // max forward = 2x speed - -void ConnexionData::focusOutEvent() { - _axisStateMap.clear(); - _buttonPressedMap.clear(); -}; - -ConnexionData& ConnexionData::getInstance() { - static ConnexionData sharedInstance; - return sharedInstance; -} - - -ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {} - - -void ConnexionData::handleAxisEvent() { - 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) { - _buttonPressedMap.clear(); - _buttonPressedMap.insert(lastButtonState); -} - -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 { - 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; - }; -} - -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()) { - return 1.0f; - } else { - return 0.0f; - } - } - return 0.0f; -} - -float ConnexionData::getAxis(int channel) const { - auto axis = _axisStateMap.find(channel); - if (axis != _axisStateMap.end()) { - return (*axis).second; - } else { - return 0.0f; - } -} - -controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) { - return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); -} - -controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) { - return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); -} - -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 -} - -ConnexionClient& ConnexionClient::getInstance() { - static ConnexionClient sharedInstance; - return sharedInstance; -} - -#ifdef HAVE_3DCONNEXIONCLIENT - -#ifdef Q_OS_WIN - -#include - -void ConnexionClient::toggleConnexion(bool shouldEnable) { - ConnexionData& connexiondata = ConnexionData::getInstance(); - if (shouldEnable && connexiondata.getDeviceID() == 0) { - init(); - } - if (!shouldEnable && connexiondata.getDeviceID() != 0) { - destroy(); - } -} - -void ConnexionClient::init() { - if (Menu::getInstance()->isOptionChecked(MenuOption::Connexion)) { - fLast3dmouseInputTime = 0; - - InitializeRawInput(GetActiveWindow()); - - QAbstractEventDispatcher::instance()->installNativeEventFilter(this); - } -} - -void ConnexionClient::destroy() { - QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); - ConnexionData& connexiondata = ConnexionData::getInstance(); - int deviceid = connexiondata.getDeviceID(); - auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(deviceid); -} - -#define LOGITECH_VENDOR_ID 0x46d - -#ifndef RIDEV_DEVNOTIFY -#define RIDEV_DEVNOTIFY 0x00002000 -#endif - -const int TRACE_RIDI_DEVICENAME = 0; -const int TRACE_RIDI_DEVICEINFO = 0; - -#ifdef _WIN64 -typedef unsigned __int64 QWORD; -#endif - -// object angular velocity per mouse tick 0.008 milliradians per second per count -static const double k3dmouseAngularVelocity = 8.0e-6; // radians per second per count - -static const int kTimeToLive = 5; - -enum ConnexionPid { - CONNEXIONPID_SPACEPILOT = 0xc625, - CONNEXIONPID_SPACENAVIGATOR = 0xc626, - CONNEXIONPID_SPACEEXPLORER = 0xc627, - CONNEXIONPID_SPACENAVIGATORFORNOTEBOOKS = 0xc628, - CONNEXIONPID_SPACEPILOTPRO = 0xc629 -}; - -// e3dmouse_virtual_key -enum V3dk { - V3DK_INVALID = 0, - V3DK_MENU = 1, V3DK_FIT, - V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, V3DK_BOTTOM, V3DK_BACK, - V3DK_CW, V3DK_CCW, - V3DK_ISO1, V3DK_ISO2, - V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6, V3DK_7, V3DK_8, V3DK_9, V3DK_10, - V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL, - V3DK_ROTATE, V3DK_PANZOOM, V3DK_DOMINANT, - V3DK_PLUS, V3DK_MINUS -}; - -struct tag_VirtualKeys { - ConnexionPid pid; - size_t nKeys; - V3dk *vkeys; -}; - -// e3dmouse_virtual_key -static const V3dk SpaceExplorerKeys[] = { - V3DK_INVALID, // there is no button 0 - V3DK_1, V3DK_2, - V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, - V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL, - V3DK_FIT, V3DK_MENU, - V3DK_PLUS, V3DK_MINUS, - V3DK_ROTATE -}; - -//e3dmouse_virtual_key -static const V3dk SpacePilotKeys[] = { - V3DK_INVALID, - V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6, - V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, - V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL, - V3DK_FIT, V3DK_MENU, - V3DK_PLUS, V3DK_MINUS, - V3DK_DOMINANT, V3DK_ROTATE, -}; - -static const struct tag_VirtualKeys _3dmouseVirtualKeys[] = { - CONNEXIONPID_SPACEPILOT, - sizeof(SpacePilotKeys) / sizeof(SpacePilotKeys[0]), - const_cast(SpacePilotKeys), - CONNEXIONPID_SPACEEXPLORER, - sizeof(SpaceExplorerKeys) / sizeof(SpaceExplorerKeys[0]), - const_cast(SpaceExplorerKeys) -}; - -// Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device to the standard 3d mouse virtual key definition. -// pid USB Product ID (PID) of 3D mouse device -// hidKeyCode Hid keycode as retrieved from a Raw Input packet -// return The standard 3d mouse virtual key (button identifier) or zero if an error occurs. - -// Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device -// to the standard 3d mouse virtual key definition. -unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) { - unsigned short virtualkey = hidKeyCode; - for (size_t i = 0; i(); - if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { - userInputMapper->registerDevice(&connexiondata); - UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); - } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) { - userInputMapper->removeDevice(connexiondata.getDeviceID()); - } - - if (!Is3dmouseAttached()) { - return false; - } - - MSG* message = (MSG*)(msg); - - if (message->message == WM_INPUT) { - HRAWINPUT hRawInput = reinterpret_cast(message->lParam); - OnRawInput(RIM_INPUT, hRawInput); - if (result != 0) { - result = 0; - } - return true; - } - return false; -} - -// Access the mouse parameters structure -I3dMouseParam& ConnexionClient::MouseParams() { - return f3dMouseParams; -} - -// Access the mouse parameters structure -const I3dMouseParam& ConnexionClient::MouseParams() const { - return f3dMouseParams; -} - -//Called with the processed motion data when a 3D mouse event is received -void ConnexionClient::Move3d(HANDLE device, std::vector& motionData) { - Q_UNUSED(device); - ConnexionData& connexiondata = ConnexionData::getInstance(); - connexiondata.cc_position = { motionData[0] * 1000, motionData[1] * 1000, motionData[2] * 1000 }; - connexiondata.cc_rotation = { motionData[3] * 1500, motionData[4] * 1500, motionData[5] * 1500 }; - connexiondata.handleAxisEvent(); -} - -//Called when a 3D mouse key is pressed -void ConnexionClient::On3dmouseKeyDown(HANDLE device, int virtualKeyCode) { - Q_UNUSED(device); - ConnexionData& connexiondata = ConnexionData::getInstance(); - connexiondata.setButton(virtualKeyCode); -} - -//Called when a 3D mouse key is released -void ConnexionClient::On3dmouseKeyUp(HANDLE device, int virtualKeyCode) { - Q_UNUSED(device); - ConnexionData& connexiondata = ConnexionData::getInstance(); - connexiondata.setButton(0); -} - -//Get an initialized array of PRAWINPUTDEVICE for the 3D devices -//pNumDevices returns the number of devices to register. Currently this is always 1. -static PRAWINPUTDEVICE GetDevicesToRegister(unsigned int* pNumDevices) { - // Array of raw input devices to register - static RAWINPUTDEVICE sRawInputDevices[] = { - { 0x01, 0x08, 0x00, 0x00 } // Usage Page = 0x01 Generic Desktop Page, Usage Id= 0x08 Multi-axis Controller - }; - - if (pNumDevices) { - *pNumDevices = sizeof(sRawInputDevices) / sizeof(sRawInputDevices[0]); - } - - return sRawInputDevices; -} - -//Detect the 3D mouse -bool ConnexionClient::Is3dmouseAttached() { - unsigned int numDevicesOfInterest = 0; - PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevicesOfInterest); - - unsigned int nDevices = 0; - - if (::GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { - return false; - } - - if (nDevices == 0) { - return false; - } - - std::vector rawInputDeviceList(nDevices); - if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) { - return false; - } - - for (unsigned int i = 0; i < nDevices; ++i) { - RID_DEVICE_INFO rdi = { sizeof(rdi) }; - unsigned int cbSize = sizeof(rdi); - - if (GetRawInputDeviceInfo(rawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) > 0) { - //skip non HID and non logitec (3DConnexion) devices - if (rdi.dwType != RIM_TYPEHID || rdi.hid.dwVendorId != LOGITECH_VENDOR_ID) { - continue; - } - - //check if devices matches Multi-axis Controller - for (unsigned int j = 0; j < numDevicesOfInterest; ++j) { - if (devicesToRegister[j].usUsage == rdi.hid.usUsage - && devicesToRegister[j].usUsagePage == rdi.hid.usUsagePage) { - return true; - } - } - } - } - return false; -} - -// Initialize the window to recieve raw-input messages -// This needs to be called initially so that Windows will send the messages from the 3D mouse to the window. -bool ConnexionClient::InitializeRawInput(HWND hwndTarget) { - fWindow = hwndTarget; - - // Simply fail if there is no window - if (!hwndTarget) { - return false; - } - - unsigned int numDevices = 0; - PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevices); - - if (numDevices == 0) { - return false; - } - - unsigned int cbSize = sizeof(devicesToRegister[0]); - for (size_t i = 0; i < numDevices; i++) { - // Set the target window to use - //devicesToRegister[i].hwndTarget = hwndTarget; - - // If Vista or newer, enable receiving the WM_INPUT_DEVICE_CHANGE message. - if (IsWindowsVistaOrGreater()) { - devicesToRegister[i].dwFlags |= RIDEV_DEVNOTIFY; - } - } - return (::RegisterRawInputDevices(devicesToRegister, numDevices, cbSize) != FALSE); -} - -//Get the raw input data from Windows -UINT ConnexionClient::GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader) { - //Includes workaround for incorrect alignment of the RAWINPUT structure on x64 os - //when running as Wow64 (copied directly from 3DConnexion code) -#ifdef _WIN64 - return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); -#else - BOOL bIsWow64 = FALSE; - ::IsWow64Process(GetCurrentProcess(), &bIsWow64); - if (!bIsWow64 || pData == NULL) { - return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); - } else { - HWND hwndTarget = fWindow; - - size_t cbDataSize = 0; - UINT nCount = 0; - PRAWINPUT pri = pData; - - MSG msg; - while (PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_NOREMOVE)) { - HRAWINPUT hRawInput = reinterpret_cast(msg.lParam); - size_t cbSize = *pcbSize - cbDataSize; - if (::GetRawInputData(hRawInput, RID_INPUT, pri, &cbSize, cbSizeHeader) == static_cast(-1)) { - if (nCount == 0) { - return static_cast(-1); - } else { - break; - } - } - ++nCount; - - // Remove the message for the data just read - PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_REMOVE); - - pri = NEXTRAWINPUTBLOCK(pri); - cbDataSize = reinterpret_cast(pri)-reinterpret_cast(pData); - if (cbDataSize >= *pcbSize) { - cbDataSize = *pcbSize; - break; - } - } - return nCount; - } -#endif -} - -// Process the raw input device data -// On3dmouseInput() does all the preprocessing of the rawinput device data before -// finally calling the Move3d method. -void ConnexionClient::On3dmouseInput() { - // Don't do any data processing in background - bool bIsForeground = (::GetActiveWindow() != NULL); - if (!bIsForeground) { - // set all cached data to zero so that a zero event is seen and the cached data deleted - for (std::map::iterator it = fDevice2Data.begin(); it != fDevice2Data.end(); it++) { - it->second.fAxes.assign(6, .0); - it->second.fIsDirty = true; - } - } - - DWORD dwNow = ::GetTickCount(); // Current time; - DWORD dwElapsedTime; // Elapsed time since we were last here - - if (0 == fLast3dmouseInputTime) { - dwElapsedTime = 10; // System timer resolution - } else { - dwElapsedTime = dwNow - fLast3dmouseInputTime; - if (fLast3dmouseInputTime > dwNow) { - dwElapsedTime = ~dwElapsedTime + 1; - } - if (dwElapsedTime<1) { - dwElapsedTime = 1; - } else if (dwElapsedTime > 500) { - // Check for wild numbers because the device was removed while sending data - dwElapsedTime = 10; - } - } - - //qDebug("On3DmouseInput() period is %dms\n", dwElapsedTime); - - float mouseData2Rotation = k3dmouseAngularVelocity; - // v = w * r, we don't know r yet so lets assume r=1.) - float mouseData2PanZoom = k3dmouseAngularVelocity; - - // Grab the I3dmouseParam interface - I3dMouseParam& i3dmouseParam = f3dMouseParams; - // Take a look at the users preferred speed setting and adjust the sensitivity accordingly - I3dMouseSensor::Speed speedSetting = i3dmouseParam.GetSpeed(); - // See "Programming for the 3D Mouse", Section 5.1.3 - float speed = (speedSetting == I3dMouseSensor::SPEED_LOW ? 0.25f : speedSetting == I3dMouseSensor::SPEED_HIGH ? 4.f : 1.f); - - // Multiplying by the following will convert the 3d mouse data to real world units - mouseData2PanZoom *= speed; - mouseData2Rotation *= speed; - - std::map::iterator iterator = fDevice2Data.begin(); - while (iterator != fDevice2Data.end()) { - - // If we have not received data for a while send a zero event - if ((--(iterator->second.fTimeToLive)) == 0) { - iterator->second.fAxes.assign(6, .0); - } else if ( !iterator->second.fIsDirty) { //!t_bPoll3dmouse && - // If we are not polling then only handle the data that was actually received - ++iterator; - continue; - } - iterator->second.fIsDirty = false; - - // get a copy of the device - HANDLE hdevice = iterator->first; - - // get a copy of the motion vectors and apply the user filters - std::vector motionData = iterator->second.fAxes; - - // apply the user filters - - // Pan Zoom filter - // See "Programming for the 3D Mouse", Section 5.1.2 - if (!i3dmouseParam.IsPanZoom()) { - // Pan zoom is switched off so set the translation vector values to zero - motionData[0] = motionData[1] = motionData[2] = 0.; - } - - // Rotate filter - // See "Programming for the 3D Mouse", Section 5.1.1 - if (!i3dmouseParam.IsRotate()) { - // Rotate is switched off so set the rotation vector values to zero - motionData[3] = motionData[4] = motionData[5] = 0.; - } - - // convert the translation vector into physical data - for (int axis = 0; axis < 3; axis++) { - motionData[axis] *= mouseData2PanZoom; - } - - // convert the directed Rotate vector into physical data - // See "Programming for the 3D Mouse", Section 7.2.2 - for (int axis = 3; axis < 6; axis++) { - motionData[axis] *= mouseData2Rotation; - } - - // Now that the data has had the filters and sensitivty settings applied - // calculate the displacements since the last view update - for (int axis = 0; axis < 6; axis++) { - motionData[axis] *= dwElapsedTime; - } - - // Now a bit of book keeping before passing on the data - if (iterator->second.IsZero()) { - iterator = fDevice2Data.erase(iterator); - } else { - ++iterator; - } - - // Work out which will be the next device - HANDLE hNextDevice = 0; - if (iterator != fDevice2Data.end()) { - hNextDevice = iterator->first; - } - - // Pass the 3dmouse input to the view controller - Move3d(hdevice, motionData); - - // Because we don't know what happened in the previous call, the cache might have - // changed so reload the iterator - iterator = fDevice2Data.find(hNextDevice); - } - - if (!fDevice2Data.empty()) { - fLast3dmouseInputTime = dwNow; - } else { - fLast3dmouseInputTime = 0; - } -} - -//Called when new raw input data is available -void ConnexionClient::OnRawInput(UINT nInputCode, HRAWINPUT hRawInput) { - const size_t cbSizeOfBuffer = 1024; - BYTE pBuffer[cbSizeOfBuffer]; - - PRAWINPUT pRawInput = reinterpret_cast(pBuffer); - UINT cbSize = cbSizeOfBuffer; - - if (::GetRawInputData(hRawInput, RID_INPUT, pRawInput, &cbSize, sizeof(RAWINPUTHEADER)) == static_cast(-1)) { - return; - } - - bool b3dmouseInput = TranslateRawInputData(nInputCode, pRawInput); - ::DefRawInputProc(&pRawInput, 1, sizeof(RAWINPUTHEADER)); - - // Check for any buffered messages - cbSize = cbSizeOfBuffer; - UINT nCount = this->GetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); - if (nCount == (UINT)-1) { - qDebug("GetRawInputBuffer returned error %d\n", GetLastError()); - } - - while (nCount>0 && nCount != static_cast(-1)) { - PRAWINPUT pri = pRawInput; - UINT nInput; - for (nInput = 0; nInputGetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); - } - - // If we have mouse input data for the app then tell tha app about it - if (b3dmouseInput) { - On3dmouseInput(); - } -} - -bool ConnexionClient::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput) { - bool bIsForeground = (nInputCode == RIM_INPUT); - - // We are not interested in keyboard or mouse data received via raw input - if (pRawInput->header.dwType != RIM_TYPEHID) { - return false; - } - - if (TRACE_RIDI_DEVICENAME == 1) { - UINT dwSize = 0; - if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, NULL, &dwSize) == 0) { - std::vector szDeviceName(dwSize + 1); - if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, &szDeviceName[0], &dwSize) > 0) { - qDebug("Device Name = %s\nDevice handle = 0x%x\n", &szDeviceName[0], pRawInput->header.hDevice); - } - } - } - - RID_DEVICE_INFO sRidDeviceInfo; - sRidDeviceInfo.cbSize = sizeof(RID_DEVICE_INFO); - UINT cbSize = sizeof(RID_DEVICE_INFO); - - if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &sRidDeviceInfo, &cbSize) == cbSize) { - if (TRACE_RIDI_DEVICEINFO == 1) { - switch (sRidDeviceInfo.dwType) { - case RIM_TYPEMOUSE: - qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEMOUSE\n"); - break; - case RIM_TYPEKEYBOARD: - qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEKEYBOARD\n"); - break; - case RIM_TYPEHID: - qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEHID\n"); - qDebug("\tVendor=0x%x\n\tProduct=0x%x\n\tUsagePage=0x%x\n\tUsage=0x%x\n", - sRidDeviceInfo.hid.dwVendorId, - sRidDeviceInfo.hid.dwProductId, - sRidDeviceInfo.hid.usUsagePage, - sRidDeviceInfo.hid.usUsage); - break; - } - } - - if (sRidDeviceInfo.hid.dwVendorId == LOGITECH_VENDOR_ID) { - if (pRawInput->data.hid.bRawData[0] == 0x01) { // Translation vector - TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; - deviceData.fTimeToLive = kTimeToLive; - if (bIsForeground) { - short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); - // Cache the pan zoom data - deviceData.fAxes[0] = static_cast(pnRawData[0]); - deviceData.fAxes[1] = static_cast(pnRawData[1]); - deviceData.fAxes[2] = static_cast(pnRawData[2]); - - //qDebug("Pan/Zoom RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[0], pnRawData[1], pnRawData[2]); - - if (pRawInput->data.hid.dwSizeHid >= 13) { // Highspeed package - // Cache the rotation data - deviceData.fAxes[3] = static_cast(pnRawData[3]); - deviceData.fAxes[4] = static_cast(pnRawData[4]); - deviceData.fAxes[5] = static_cast(pnRawData[5]); - deviceData.fIsDirty = true; - - //qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[3], pnRawData[4], pnRawData[5]); - return true; - } - } else { // Zero out the data if the app is not in forground - deviceData.fAxes.assign(6, 0.f); - } - } else if (pRawInput->data.hid.bRawData[0] == 0x02) { // Rotation vector - // If we are not in foreground do nothing - // The rotation vector was zeroed out with the translation vector in the previous message - if (bIsForeground) { - TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; - deviceData.fTimeToLive = kTimeToLive; - - short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); - // Cache the rotation data - deviceData.fAxes[3] = static_cast(pnRawData[0]); - deviceData.fAxes[4] = static_cast(pnRawData[1]); - deviceData.fAxes[5] = static_cast(pnRawData[2]); - deviceData.fIsDirty = true; - - //qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[0], pnRawData[1], pnRawData[2]); - - return true; - } - } else if (pRawInput->data.hid.bRawData[0] == 0x03) { // Keystate change - // this is a package that contains 3d mouse keystate information - // bit0=key1, bit=key2 etc. - - unsigned long dwKeystate = *reinterpret_cast(&pRawInput->data.hid.bRawData[1]); - - //qDebug("ButtonData =0x%x\n", dwKeystate); - - // Log the keystate changes - unsigned long dwOldKeystate = fDevice2Keystate[pRawInput->header.hDevice]; - if (dwKeystate != 0) { - fDevice2Keystate[pRawInput->header.hDevice] = dwKeystate; - } else { - fDevice2Keystate.erase(pRawInput->header.hDevice); - } - - // Only call the keystate change handlers if the app is in foreground - if (bIsForeground) { - unsigned long dwChange = dwKeystate ^ dwOldKeystate; - - for (int nKeycode = 1; nKeycode<33; nKeycode++) { - if (dwChange & 0x01) { - int nVirtualKeyCode = HidToVirtualKey(sRidDeviceInfo.hid.dwProductId, nKeycode); - if (nVirtualKeyCode) { - if (dwKeystate & 0x01) { - On3dmouseKeyDown(pRawInput->header.hDevice, nVirtualKeyCode); - } else { - On3dmouseKeyUp(pRawInput->header.hDevice, nVirtualKeyCode); - } - } - } - dwChange >>= 1; - dwKeystate >>= 1; - } - } - } - } - } - return false; -} - -MouseParameters::MouseParameters() : - fNavigation(NAVIGATION_OBJECT_MODE), - fPivot(PIVOT_AUTO), - fPivotVisibility(PIVOT_SHOW), - fIsLockHorizon(true), - fIsPanZoom(true), - fIsRotate(true), - fSpeed(SPEED_LOW) -{ -} - -bool MouseParameters::IsPanZoom() const { - return fIsPanZoom; -} - -bool MouseParameters::IsRotate() const { - return fIsRotate; -} - -MouseParameters::Speed MouseParameters::GetSpeed() const { - return fSpeed; -} - -void MouseParameters::SetPanZoom(bool isPanZoom) { - fIsPanZoom = isPanZoom; -} - -void MouseParameters::SetRotate(bool isRotate) { - fIsRotate = isRotate; -} - -void MouseParameters::SetSpeed(Speed speed) { - fSpeed = speed; -} - -MouseParameters::Navigation MouseParameters::GetNavigationMode() const { - return fNavigation; -} - -MouseParameters::Pivot MouseParameters::GetPivotMode() const { - return fPivot; -} - -MouseParameters::PivotVisibility MouseParameters::GetPivotVisibility() const { - return fPivotVisibility; -} - -bool MouseParameters::IsLockHorizon() const { - return fIsLockHorizon; -} - -void MouseParameters::SetLockHorizon(bool bOn) { - fIsLockHorizon=bOn; -} - -void MouseParameters::SetNavigationMode(Navigation navigation) { - fNavigation=navigation; -} - -void MouseParameters::SetPivotMode(Pivot pivot) { - if (fPivot!=PIVOT_MANUAL || pivot!=PIVOT_AUTO_OVERRIDE) { - fPivot = pivot; - } -} - -void MouseParameters::SetPivotVisibility(PivotVisibility visibility) { - fPivotVisibility = visibility; -} - -#else - -int fConnexionClientID; - -static ConnexionDeviceState lastState; - -static void DeviceAddedHandler(unsigned int connection); -static void DeviceRemovedHandler(unsigned int connection); -static void MessageHandler(unsigned int connection, unsigned int messageType, void *messageArgument); - -void ConnexionClient::toggleConnexion(bool shouldEnable) { - if (shouldEnable && !Is3dmouseAttached()) { - init(); - } - if (!shouldEnable && Is3dmouseAttached()) { - destroy(); - } -} - -void ConnexionClient::init() { - // Make sure the framework is installed - if (Menu::getInstance()->isOptionChecked(MenuOption::Connexion)) { - // Install message handler and register our client - InstallConnexionHandlers(MessageHandler, DeviceAddedHandler, DeviceRemovedHandler); - // Either use this to take over in our application only... does not work - // fConnexionClientID = RegisterConnexionClient('MCTt', "\pConnexion Client Test", kConnexionClientModeTakeOver, kConnexionMaskAll); - - // ...or use this to take over system-wide - fConnexionClientID = RegisterConnexionClient(kConnexionClientWildcard, NULL, kConnexionClientModeTakeOver, kConnexionMaskAll); - ConnexionData& connexiondata = ConnexionData::getInstance(); - memcpy(&connexiondata.clientId, &fConnexionClientID, (long)sizeof(int)); - - // A separate API call is required to capture buttons beyond the first 8 - SetConnexionClientButtonMask(fConnexionClientID, kConnexionMaskAllButtons); - - // use default switches - ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchesDisabled, NULL); - - if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(&connexiondata); - UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); - } - //let one axis be dominant - //ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchDominant | kConnexionSwitchEnableAll, NULL); - } -} - -void ConnexionClient::destroy() { - // Make sure the framework is installed - if (&InstallConnexionHandlers != NULL) { - // Unregister our client and clean up all handlers - if (fConnexionClientID) { - UnregisterConnexionClient(fConnexionClientID); - } - CleanupConnexionHandlers(); - fConnexionClientID = 0; - ConnexionData& connexiondata = ConnexionData::getInstance(); - if (connexiondata.getDeviceID()!=0) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(connexiondata.getDeviceID()); - connexiondata.setDeviceID(0); - } - } -} - -void DeviceAddedHandler(unsigned int connection) { - ConnexionData& connexiondata = ConnexionData::getInstance(); - if (connexiondata.getDeviceID() == 0) { - qCWarning(interfaceapp) << "3Dconnexion device added "; - auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(&connexiondata); - UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); - } -} - -void DeviceRemovedHandler(unsigned int connection) { - ConnexionData& connexiondata = ConnexionData::getInstance(); - if (connexiondata.getDeviceID() != 0) { - qCWarning(interfaceapp) << "3Dconnexion device removed"; - auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(connexiondata.getDeviceID()); - connexiondata.setDeviceID(0); - } -} - -bool ConnexionClient::Is3dmouseAttached() { - int result; - if (fConnexionClientID) { - if (ConnexionControl(kConnexionCtlGetDeviceID, 0, &result)) { - return false; - } - return true; - } - return false; -} - -void MessageHandler(unsigned int connection, unsigned int messageType, void *messageArgument) { - ConnexionDeviceState *state; - - switch (messageType) { - case kConnexionMsgDeviceState: - state = (ConnexionDeviceState*)messageArgument; - if (state->client == fConnexionClientID) { - ConnexionData& connexiondata = ConnexionData::getInstance(); - connexiondata.cc_position = { state->axis[0], state->axis[1], state->axis[2] }; - connexiondata.cc_rotation = { state->axis[3], state->axis[4], state->axis[5] }; - - connexiondata.handleAxisEvent(); - if (state->buttons != lastState.buttons) { - connexiondata.setButton(state->buttons); - } - memmove(&lastState, state, (long)sizeof(ConnexionDeviceState)); - } - break; - case kConnexionMsgPrefsChanged: - // the prefs have changed, do something - break; - default: - // other messageTypes can happen and should be ignored - break; - } - -} - -#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 deleted file mode 100755 index 03a43d4c64..0000000000 --- a/interface/src/devices/3DConnexionClient.h +++ /dev/null @@ -1,223 +0,0 @@ -// 3DConnexionClient.h -// interface/src/devices -// -// Created by Marcel Verhagen on 09-06-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 -// - -#ifndef hifi_3DConnexionClient_h -#define hifi_3DConnexionClient_h - -#if 0 -#include -#include -#include - -#include "InterfaceLogging.h" - -#ifndef HAVE_3DCONNEXIONCLIENT -class ConnexionClient : public QObject { - Q_OBJECT -public: - static ConnexionClient& getInstance(); - void init() {}; - void destroy() {}; - bool Is3dmouseAttached() { return false; }; -public slots: - void toggleConnexion(bool shouldEnable) {}; -}; -#endif // NOT_HAVE_3DCONNEXIONCLIENT - -#ifdef HAVE_3DCONNEXIONCLIENT -// the windows connexion rawinput -#ifdef Q_OS_WIN - -#include "I3dMouseParams.h" -#include -#include -#include -#include - -// windows rawinput parameters -class MouseParameters : public I3dMouseParam { -public: - MouseParameters(); - - // I3dmouseSensor interface - bool IsPanZoom() const; - bool IsRotate() const; - Speed GetSpeed() const; - - void SetPanZoom(bool isPanZoom); - void SetRotate(bool isRotate); - void SetSpeed(Speed speed); - - // I3dmouseNavigation interface - Navigation GetNavigationMode() const; - Pivot GetPivotMode() const; - PivotVisibility GetPivotVisibility() const; - bool IsLockHorizon() const; - - void SetLockHorizon(bool bOn); - void SetNavigationMode(Navigation navigation); - void SetPivotMode(Pivot pivot); - void SetPivotVisibility(PivotVisibility visibility); - - static bool Is3dmouseAttached(); - -private: - MouseParameters(const MouseParameters&); - const MouseParameters& operator = (const MouseParameters&); - - Navigation fNavigation; - Pivot fPivot; - PivotVisibility fPivotVisibility; - bool fIsLockHorizon; - - bool fIsPanZoom; - bool fIsRotate; - Speed fSpeed; -}; - -class ConnexionClient : public QObject, public QAbstractNativeEventFilter { - Q_OBJECT -public: - ConnexionClient() {}; - - static ConnexionClient& getInstance(); - void init(); - void destroy(); - bool Is3dmouseAttached(); - - ConnexionClient* client; - - I3dMouseParam& MouseParams(); - const I3dMouseParam& MouseParams() const; - - virtual void Move3d(HANDLE device, std::vector& motionData); - virtual void On3dmouseKeyDown(HANDLE device, int virtualKeyCode); - virtual void On3dmouseKeyUp(HANDLE device, int virtualKeyCode); - - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) Q_DECL_OVERRIDE - { - MSG* msg = static_cast< MSG * >(message); - return RawInputEventFilter(message, result); - } - -public slots: - void toggleConnexion(bool shouldEnable); - -signals: - void Move3d(std::vector& motionData); - void On3dmouseKeyDown(int virtualKeyCode); - void On3dmouseKeyUp(int virtualKeyCode); - -private: - bool InitializeRawInput(HWND hwndTarget); - - bool RawInputEventFilter(void* msg, long* result); - - void OnRawInput(UINT nInputCode, HRAWINPUT hRawInput); - UINT GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader); - bool TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput); - void On3dmouseInput(); - - class TInputData { - public: - TInputData() : fAxes(6) {} - - bool IsZero() { - return (0.0f == fAxes[0] && 0.0f == fAxes[1] && 0.0f == fAxes[2] && - 0.0f == fAxes[3] && 0.0f == fAxes[4] && 0.0f == fAxes[5]); - } - - int fTimeToLive; // For telling if the device was unplugged while sending data - bool fIsDirty; - std::vector fAxes; - - }; - - HWND fWindow; - - // Data cache to handle multiple rawinput devices - std::map< HANDLE, TInputData> fDevice2Data; - std::map< HANDLE, unsigned long> fDevice2Keystate; - - // 3dmouse parameters - MouseParameters f3dMouseParams; // Rotate, Pan Zoom etc. - - // use to calculate distance traveled since last event - DWORD fLast3dmouseInputTime; -}; - -// the osx connexion api -#else - -#include -#include "ConnexionClientAPI.h" - -class ConnexionClient : public QObject { - Q_OBJECT -public: - static ConnexionClient& getInstance(); - void init(); - void destroy(); - bool Is3dmouseAttached(); -public slots: - void toggleConnexion(bool shouldEnable); -}; - -#endif // __APPLE__ - -#endif // HAVE_3DCONNEXIONCLIENT - - -// connnects to the userinputmapper -class ConnexionData : public QObject, public controller::InputDevice { - Q_OBJECT - -public: - static ConnexionData& getInstance(); - ConnexionData(); - enum PositionChannel { - TRANSLATE_X, - TRANSLATE_Y, - TRANSLATE_Z, - ROTATE_X, - ROTATE_Y, - ROTATE_Z, - }; - - enum ButtonChannel { - BUTTON_1 = 1, - BUTTON_2 = 2, - BUTTON_3 = 3 - }; - - typedef std::unordered_set ButtonPressedMap; - typedef std::map AxisStateMap; - - float getButton(int channel) const; - float getAxis(int channel) const; - - 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; - int clientId; - - void setButton(int lastButtonState); - void handleAxisEvent(); -}; - -#endif - -#endif // defined(hifi_3DConnexionClient_h) From 5d695da630dbdabfd9fdcd72d188ce15fbaec674 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Mon, 2 Nov 2015 23:31:10 -0800 Subject: [PATCH 0643/1003] Do not merge - Spacemouse --- .../Spacemouse/spacemouseExample.js | 72 ++ .../resources/controllers/spacemouse.json | 15 + .../src/input-plugins/SpacemouseManager.cpp | 1022 +++++++++++++++++ .../src/input-plugins/SpacemouseManager.h | 236 ++++ 4 files changed, 1345 insertions(+) create mode 100644 examples/controllers/Spacemouse/spacemouseExample.js create mode 100644 interface/resources/controllers/spacemouse.json create mode 100644 libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp create mode 100644 libraries/input-plugins/src/input-plugins/SpacemouseManager.h diff --git a/examples/controllers/Spacemouse/spacemouseExample.js b/examples/controllers/Spacemouse/spacemouseExample.js new file mode 100644 index 0000000000..904f29cd75 --- /dev/null +++ b/examples/controllers/Spacemouse/spacemouseExample.js @@ -0,0 +1,72 @@ +// +// spaceMouseDebug.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 +// + + + +var firstmove = 1; +var position = { + x: 0, + y: 0, + z: 0 + }; +var rotation = { + x: 0, + y: 0, + z: 0 + }; + +function toggleFirstMove() { + if(firstmove){ + print("____________________________________"); + firstmove = 0; + } +} + +function spacemouseCheck() { + return Controller.Hardware.Spacemouse !== undefined; +} + +function update(deltaTime) { + if(spacemouseCheck){ + if(Controller.getValue(Controller.Standard.LY) != 0){ + toggleFirstMove(); + print("Controller TX: " + Controller.getValue(Controller.Standard.LY)); + } + + if(Controller.getValue(Controller.Standard.LX) != 0){ + toggleFirstMove(); + print("Controller TZ: " + Controller.getValue(Controller.Standard.LX)); + } + + if(Controller.getValue(Controller.Standard.LT) != 0){ + toggleFirstMove(); + print("Controller TY: " + Controller.getValue(Controller.Standard.LT)); + } + + if(Controller.getValue(Controller.Standard.RY) != 0){ + toggleFirstMove(); + print("Controller RX: " + Controller.getValue(Controller.Standard.RY)); + } + + if(Controller.getValue(Controller.Standard.RX) != 0){ + toggleFirstMove(); + print("Controller RZ: " + Controller.getValue(Controller.Standard.RX)); + } + + if(Controller.getValue(Controller.Standard.RT) != 0){ + toggleFirstMove(); + print("Controller RY: " + Controller.getValue(Controller.Standard.RT)); + } + + firstmove = 1; + } +} + +Script.update.connect(update); \ No newline at end of file diff --git a/interface/resources/controllers/spacemouse.json b/interface/resources/controllers/spacemouse.json new file mode 100644 index 0000000000..61af605512 --- /dev/null +++ b/interface/resources/controllers/spacemouse.json @@ -0,0 +1,15 @@ +{ + "name": "Spacemouse to Standard", + "channels": [ + { "from": "Spacemouse.TranslateX", "to": "Standard.LY" }, + { "from": "Spacemouse.TranslateZ", "filters": "invert", "to": "Standard.LX" }, + + { "from": "Spacemouse.RotateX", "to": "Standard.RY" }, + { "from": "Spacemouse.RotateZ", "to": "Standard.RX" }, + + { "from": "Spacemouse.LeftButton", "to": "Standard.Back" }, + { "from": "Spacemouse.RightButton", "to": "Standard.Start" } + + ] +} + diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp new file mode 100644 index 0000000000..3078f99a23 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -0,0 +1,1022 @@ +// +// SpacemouseManager.cpp +// interface/src/devices +// +// Created by MarcelEdward Verhagen on 09-06-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 "SpacemouseManager.h" + +#include +#include + +#include +#include + +#include "../../../interface/src/Menu.h" + +const float MAX_AXIS = 75.0f; // max forward = 2x speed + +static std::shared_ptr instance = NULL; +SpacemouseDevice::SpacemouseDevice() : +InputDevice("Spacemouse") +{ + instance = std::shared_ptr(this); +} + +void SpacemouseDevice::focusOutEvent() { + _axisStateMap.clear(); + _buttonPressedMap.clear(); +}; + + +//SpacemouseDevice& SpacemouseDevice::getInstance() { +// static SpacemouseDevice sharedInstance; +// return sharedInstance; +//} + + + +//std::shared_ptr SpacemouseManager::getDevice(){ +// return instance; +//} + +//SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") {} + + +void SpacemouseDevice::handleAxisEvent() { + 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 SpacemouseDevice::setButton(int lastButtonState) { + _buttonPressedMap.clear(); + _buttonPressedMap.insert(lastButtonState); +} + + +controller::Input::NamedVector SpacemouseDevice::getAvailableInputs() const { + using namespace controller; + + + static const Input::NamedVector availableInputs{ + + makePair(BUTTON_1, "LeftButton"), + makePair(BUTTON_2, "RightButton"), + //makePair(BUTTON_3, "BothButtons"), + makePair(TRANSLATE_X, "TranslateX"), + //makePair(TRANSLATE_Y, "TranslateY"), + makePair(TRANSLATE_Z, "TranslateZ"), + makePair(ROTATE_X, "RotateX"), + //makePair(ROTATE_Y, "RotateY"), + makePair(ROTATE_Z, "RotateZ"), + + }; + return availableInputs; +} + +QString SpacemouseDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/spacemouse.json"; + return MAPPING_JSON; +} + +//void SpacemouseDevice::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 SpacemouseDevice::getButton(int channel) const { + if (!_buttonPressedMap.empty()) { + if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { + return 1.0f; + } + else { + return 0.0f; + } + } + return 0.0f; +} + +float SpacemouseDevice::getAxis(int channel) const { + auto axis = _axisStateMap.find(channel); + if (axis != _axisStateMap.end()) { + return (*axis).second; + } + else { + return 0.0f; + } +} + +controller::Input SpacemouseDevice::makeInput(SpacemouseDevice::ButtonChannel button) const { + return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); +} + +controller::Input SpacemouseDevice::makeInput(SpacemouseDevice::PositionChannel axis) const { + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); +} + +controller::Input::NamedPair SpacemouseDevice::makePair(SpacemouseDevice::ButtonChannel button, const QString& name) const { + return controller::Input::NamedPair(makeInput(button), name); +} + +controller::Input::NamedPair SpacemouseDevice::makePair(SpacemouseDevice::PositionChannel axis, const QString& name) const { + return controller::Input::NamedPair(makeInput(axis), name); +} + +void SpacemouseDevice::update(float deltaTime, bool jointsCaptured) { + // the update is done in the SpacemouseManager 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 +} + +SpacemouseManager& SpacemouseManager::getInstance() { + static SpacemouseManager sharedInstance; + if (instance == NULL){ + new SpacemouseDevice(); + } + return sharedInstance; +} + +void SpacemouseManager::ManagerFocusOutEvent() { + instance->focusOutEvent(); +} + +#ifdef HAVE_SPACEMOUSE + +#ifdef Q_OS_WIN + +#include + + + +void SpacemouseManager::toggleSpacemouse(bool shouldEnable) { + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + //if (shouldEnable && spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { + if (shouldEnable && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { + init(); + } + if (!shouldEnable && instance->getDeviceID() != controller::Input::INVALID_DEVICE) { + destroy(); + } +} + +void SpacemouseManager::init() { + if (Menu::getInstance()->isOptionChecked(MenuOption::Connexion)) { + fLast3dmouseInputTime = 0; + + InitializeRawInput(GetActiveWindow()); + + QAbstractEventDispatcher::instance()->installNativeEventFilter(this); + } +} + +void SpacemouseManager::destroy() { + QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + //int deviceid = spacemousedevice.getDeviceID(); + int deviceid = instance->getDeviceID(); + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(deviceid); +} + +#define LOGITECH_VENDOR_ID 0x46d + +#ifndef RIDEV_DEVNOTIFY +#define RIDEV_DEVNOTIFY 0x00002000 +#endif + +const int TRACE_RIDI_DEVICENAME = 0; +const int TRACE_RIDI_DEVICEINFO = 0; + +#ifdef _WIN64 +typedef unsigned __int64 QWORD; +#endif + +// object angular velocity per mouse tick 0.008 milliradians per second per count +static const double k3dmouseAngularVelocity = 8.0e-6; // radians per second per count + +static const int kTimeToLive = 5; + +enum ConnexionPid { + CONNEXIONPID_SPACEPILOT = 0xc625, + CONNEXIONPID_SPACENAVIGATOR = 0xc626, + CONNEXIONPID_SPACEEXPLORER = 0xc627, + CONNEXIONPID_SPACENAVIGATORFORNOTEBOOKS = 0xc628, + CONNEXIONPID_SPACEPILOTPRO = 0xc629 +}; + +// e3dmouse_virtual_key +enum V3dk { + V3DK_INVALID = 0, + V3DK_MENU = 1, V3DK_FIT, + V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, V3DK_BOTTOM, V3DK_BACK, + V3DK_CW, V3DK_CCW, + V3DK_ISO1, V3DK_ISO2, + V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6, V3DK_7, V3DK_8, V3DK_9, V3DK_10, + V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL, + V3DK_ROTATE, V3DK_PANZOOM, V3DK_DOMINANT, + V3DK_PLUS, V3DK_MINUS +}; + +struct tag_VirtualKeys { + ConnexionPid pid; + size_t nKeys; + V3dk *vkeys; +}; + +// e3dmouse_virtual_key +static const V3dk SpaceExplorerKeys[] = { + V3DK_INVALID, // there is no button 0 + V3DK_1, V3DK_2, + V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, + V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL, + V3DK_FIT, V3DK_MENU, + V3DK_PLUS, V3DK_MINUS, + V3DK_ROTATE +}; + +//e3dmouse_virtual_key +static const V3dk SpacePilotKeys[] = { + V3DK_INVALID, + V3DK_1, V3DK_2, V3DK_3, V3DK_4, V3DK_5, V3DK_6, + V3DK_TOP, V3DK_LEFT, V3DK_RIGHT, V3DK_FRONT, + V3DK_ESC, V3DK_ALT, V3DK_SHIFT, V3DK_CTRL, + V3DK_FIT, V3DK_MENU, + V3DK_PLUS, V3DK_MINUS, + V3DK_DOMINANT, V3DK_ROTATE, +}; + +static const struct tag_VirtualKeys _3dmouseVirtualKeys[] = { + CONNEXIONPID_SPACEPILOT, + sizeof(SpacePilotKeys) / sizeof(SpacePilotKeys[0]), + const_cast(SpacePilotKeys), + CONNEXIONPID_SPACEEXPLORER, + sizeof(SpaceExplorerKeys) / sizeof(SpaceExplorerKeys[0]), + const_cast(SpaceExplorerKeys) +}; + +// Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device to the standard 3d mouse virtual key definition. +// pid USB Product ID (PID) of 3D mouse device +// hidKeyCode Hid keycode as retrieved from a Raw Input packet +// return The standard 3d mouse virtual key (button identifier) or zero if an error occurs. + +// Converts a hid device keycode (button identifier) of a pre-2009 3Dconnexion USB device +// to the standard 3d mouse virtual key definition. +unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) { + unsigned short virtualkey = hidKeyCode; + for (size_t i = 0; i(); + //if (Is3dmouseAttached() && spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { + if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { + userInputMapper->registerDevice(instance); + UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); + } + //else if (!Is3dmouseAttached() && spacemousedevice.getDeviceID() != controller::Input::INVALID_DEVICE) { + else if (!Is3dmouseAttached() && instance->getDeviceID() != controller::Input::INVALID_DEVICE) { + userInputMapper->removeDevice(instance->getDeviceID()); + } + + if (!Is3dmouseAttached()) { + return false; + } + + MSG* message = (MSG*)(msg); + + if (message->message == WM_INPUT) { + HRAWINPUT hRawInput = reinterpret_cast(message->lParam); + OnRawInput(RIM_INPUT, hRawInput); + if (result != 0) { + result = 0; + } + return true; + } + return false; +} + +// Access the mouse parameters structure +I3dMouseParam& SpacemouseManager::MouseParams() { + return f3dMouseParams; +} + +// Access the mouse parameters structure +const I3dMouseParam& SpacemouseManager::MouseParams() const { + return f3dMouseParams; +} + +//Called with the processed motion data when a 3D mouse event is received +void SpacemouseManager::Move3d(HANDLE device, std::vector& motionData) { + Q_UNUSED(device); + instance->cc_position = { motionData[0] * 1000, motionData[1] * 1000, motionData[2] * 1000 }; + instance->cc_rotation = { motionData[3] * 1500, motionData[4] * 1500, motionData[5] * 1500 }; + instance->handleAxisEvent(); +} + +//Called when a 3D mouse key is pressed +void SpacemouseManager::On3dmouseKeyDown(HANDLE device, int virtualKeyCode) { + Q_UNUSED(device); + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + instance->setButton(virtualKeyCode); +} + +//Called when a 3D mouse key is released +void SpacemouseManager::On3dmouseKeyUp(HANDLE device, int virtualKeyCode) { + Q_UNUSED(device); + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + instance->setButton(0); +} + +//Get an initialized array of PRAWINPUTDEVICE for the 3D devices +//pNumDevices returns the number of devices to register. Currently this is always 1. +static PRAWINPUTDEVICE GetDevicesToRegister(unsigned int* pNumDevices) { + // Array of raw input devices to register + static RAWINPUTDEVICE sRawInputDevices[] = { + { 0x01, 0x08, 0x00, 0x00 } // Usage Page = 0x01 Generic Desktop Page, Usage Id= 0x08 Multi-axis Controller + }; + + if (pNumDevices) { + *pNumDevices = sizeof(sRawInputDevices) / sizeof(sRawInputDevices[0]); + } + + return sRawInputDevices; +} + +//Detect the 3D mouse +bool SpacemouseManager::Is3dmouseAttached() { + unsigned int numDevicesOfInterest = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevicesOfInterest); + + unsigned int nDevices = 0; + + if (::GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { + return false; + } + + if (nDevices == 0) { + return false; + } + + std::vector rawInputDeviceList(nDevices); + if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) { + return false; + } + + for (unsigned int i = 0; i < nDevices; ++i) { + RID_DEVICE_INFO rdi = { sizeof(rdi) }; + unsigned int cbSize = sizeof(rdi); + + if (GetRawInputDeviceInfo(rawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) > 0) { + //skip non HID and non logitec (3DConnexion) devices + if (rdi.dwType != RIM_TYPEHID || rdi.hid.dwVendorId != LOGITECH_VENDOR_ID) { + continue; + } + + //check if devices matches Multi-axis Controller + for (unsigned int j = 0; j < numDevicesOfInterest; ++j) { + if (devicesToRegister[j].usUsage == rdi.hid.usUsage + && devicesToRegister[j].usUsagePage == rdi.hid.usUsagePage) { + return true; + } + } + } + } + return false; +} + +// Initialize the window to recieve raw-input messages +// This needs to be called initially so that Windows will send the messages from the 3D mouse to the window. +bool SpacemouseManager::InitializeRawInput(HWND hwndTarget) { + fWindow = hwndTarget; + + // Simply fail if there is no window + if (!hwndTarget) { + return false; + } + + unsigned int numDevices = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevices); + + if (numDevices == 0) { + return false; + } + + unsigned int cbSize = sizeof(devicesToRegister[0]); + for (size_t i = 0; i < numDevices; i++) { + // Set the target window to use + //devicesToRegister[i].hwndTarget = hwndTarget; + + // If Vista or newer, enable receiving the WM_INPUT_DEVICE_CHANGE message. + if (IsWindowsVistaOrGreater()) { + devicesToRegister[i].dwFlags |= RIDEV_DEVNOTIFY; + } + } + return (::RegisterRawInputDevices(devicesToRegister, numDevices, cbSize) != FALSE); +} + +//Get the raw input data from Windows +UINT SpacemouseManager::GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader) { + //Includes workaround for incorrect alignment of the RAWINPUT structure on x64 os + //when running as Wow64 (copied directly from 3DConnexion code) +#ifdef _WIN64 + return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); +#else + BOOL bIsWow64 = FALSE; + ::IsWow64Process(GetCurrentProcess(), &bIsWow64); + if (!bIsWow64 || pData == NULL) { + return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); + } + else { + HWND hwndTarget = fWindow; + + size_t cbDataSize = 0; + UINT nCount = 0; + PRAWINPUT pri = pData; + + MSG msg; + while (PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_NOREMOVE)) { + HRAWINPUT hRawInput = reinterpret_cast(msg.lParam); + size_t cbSize = *pcbSize - cbDataSize; + if (::GetRawInputData(hRawInput, RID_INPUT, pri, &cbSize, cbSizeHeader) == static_cast(-1)) { + if (nCount == 0) { + return static_cast(-1); + } + else { + break; + } + } + ++nCount; + + // Remove the message for the data just read + PeekMessage(&msg, hwndTarget, WM_INPUT, WM_INPUT, PM_REMOVE); + + pri = NEXTRAWINPUTBLOCK(pri); + cbDataSize = reinterpret_cast(pri)-reinterpret_cast(pData); + if (cbDataSize >= *pcbSize) { + cbDataSize = *pcbSize; + break; + } + } + return nCount; + } +#endif +} + +// Process the raw input device data +// On3dmouseInput() does all the preprocessing of the rawinput device data before +// finally calling the Move3d method. +void SpacemouseManager::On3dmouseInput() { + // Don't do any data processing in background + bool bIsForeground = (::GetActiveWindow() != NULL); + if (!bIsForeground) { + // set all cached data to zero so that a zero event is seen and the cached data deleted + for (std::map::iterator it = fDevice2Data.begin(); it != fDevice2Data.end(); it++) { + it->second.fAxes.assign(6, .0); + it->second.fIsDirty = true; + } + } + + DWORD dwNow = ::GetTickCount(); // Current time; + DWORD dwElapsedTime; // Elapsed time since we were last here + + if (0 == fLast3dmouseInputTime) { + dwElapsedTime = 10; // System timer resolution + } + else { + dwElapsedTime = dwNow - fLast3dmouseInputTime; + if (fLast3dmouseInputTime > dwNow) { + dwElapsedTime = ~dwElapsedTime + 1; + } + if (dwElapsedTime<1) { + dwElapsedTime = 1; + } + else if (dwElapsedTime > 500) { + // Check for wild numbers because the device was removed while sending data + dwElapsedTime = 10; + } + } + + //qDebug("On3DmouseInput() period is %dms\n", dwElapsedTime); + + float mouseData2Rotation = k3dmouseAngularVelocity; + // v = w * r, we don't know r yet so lets assume r=1.) + float mouseData2PanZoom = k3dmouseAngularVelocity; + + // Grab the I3dmouseParam interface + I3dMouseParam& i3dmouseParam = f3dMouseParams; + // Take a look at the users preferred speed setting and adjust the sensitivity accordingly + I3dMouseSensor::Speed speedSetting = i3dmouseParam.GetSpeed(); + // See "Programming for the 3D Mouse", Section 5.1.3 + float speed = (speedSetting == I3dMouseSensor::SPEED_LOW ? 0.25f : speedSetting == I3dMouseSensor::SPEED_HIGH ? 4.f : 1.f); + + // Multiplying by the following will convert the 3d mouse data to real world units + mouseData2PanZoom *= speed; + mouseData2Rotation *= speed; + + std::map::iterator iterator = fDevice2Data.begin(); + while (iterator != fDevice2Data.end()) { + + // If we have not received data for a while send a zero event + if ((--(iterator->second.fTimeToLive)) == 0) { + iterator->second.fAxes.assign(6, .0); + } + else if (!iterator->second.fIsDirty) { //!t_bPoll3dmouse && + // If we are not polling then only handle the data that was actually received + ++iterator; + continue; + } + iterator->second.fIsDirty = false; + + // get a copy of the device + HANDLE hdevice = iterator->first; + + // get a copy of the motion vectors and apply the user filters + std::vector motionData = iterator->second.fAxes; + + // apply the user filters + + // Pan Zoom filter + // See "Programming for the 3D Mouse", Section 5.1.2 + if (!i3dmouseParam.IsPanZoom()) { + // Pan zoom is switched off so set the translation vector values to zero + motionData[0] = motionData[1] = motionData[2] = 0.; + } + + // Rotate filter + // See "Programming for the 3D Mouse", Section 5.1.1 + if (!i3dmouseParam.IsRotate()) { + // Rotate is switched off so set the rotation vector values to zero + motionData[3] = motionData[4] = motionData[5] = 0.; + } + + // convert the translation vector into physical data + for (int axis = 0; axis < 3; axis++) { + motionData[axis] *= mouseData2PanZoom; + } + + // convert the directed Rotate vector into physical data + // See "Programming for the 3D Mouse", Section 7.2.2 + for (int axis = 3; axis < 6; axis++) { + motionData[axis] *= mouseData2Rotation; + } + + // Now that the data has had the filters and sensitivty settings applied + // calculate the displacements since the last view update + for (int axis = 0; axis < 6; axis++) { + motionData[axis] *= dwElapsedTime; + } + + // Now a bit of book keeping before passing on the data + if (iterator->second.IsZero()) { + iterator = fDevice2Data.erase(iterator); + } + else { + ++iterator; + } + + // Work out which will be the next device + HANDLE hNextDevice = 0; + if (iterator != fDevice2Data.end()) { + hNextDevice = iterator->first; + } + + // Pass the 3dmouse input to the view controller + Move3d(hdevice, motionData); + + // Because we don't know what happened in the previous call, the cache might have + // changed so reload the iterator + iterator = fDevice2Data.find(hNextDevice); + } + + if (!fDevice2Data.empty()) { + fLast3dmouseInputTime = dwNow; + } + else { + fLast3dmouseInputTime = 0; + } +} + +//Called when new raw input data is available +void SpacemouseManager::OnRawInput(UINT nInputCode, HRAWINPUT hRawInput) { + const size_t cbSizeOfBuffer = 1024; + BYTE pBuffer[cbSizeOfBuffer]; + + PRAWINPUT pRawInput = reinterpret_cast(pBuffer); + UINT cbSize = cbSizeOfBuffer; + + if (::GetRawInputData(hRawInput, RID_INPUT, pRawInput, &cbSize, sizeof(RAWINPUTHEADER)) == static_cast(-1)) { + return; + } + + bool b3dmouseInput = TranslateRawInputData(nInputCode, pRawInput); + ::DefRawInputProc(&pRawInput, 1, sizeof(RAWINPUTHEADER)); + + // Check for any buffered messages + cbSize = cbSizeOfBuffer; + UINT nCount = this->GetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); + if (nCount == (UINT)-1) { + qDebug("GetRawInputBuffer returned error %d\n", GetLastError()); + } + + while (nCount>0 && nCount != static_cast(-1)) { + PRAWINPUT pri = pRawInput; + UINT nInput; + for (nInput = 0; nInputGetRawInputBuffer(pRawInput, &cbSize, sizeof(RAWINPUTHEADER)); + } + + // If we have mouse input data for the app then tell tha app about it + if (b3dmouseInput) { + On3dmouseInput(); + } +} + +bool SpacemouseManager::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput) { + bool bIsForeground = (nInputCode == RIM_INPUT); + + // We are not interested in keyboard or mouse data received via raw input + if (pRawInput->header.dwType != RIM_TYPEHID) { + return false; + } + + if (TRACE_RIDI_DEVICENAME == 1) { + UINT dwSize = 0; + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, NULL, &dwSize) == 0) { + std::vector szDeviceName(dwSize + 1); + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICENAME, &szDeviceName[0], &dwSize) > 0) { + qDebug("Device Name = %s\nDevice handle = 0x%x\n", &szDeviceName[0], pRawInput->header.hDevice); + } + } + } + + RID_DEVICE_INFO sRidDeviceInfo; + sRidDeviceInfo.cbSize = sizeof(RID_DEVICE_INFO); + UINT cbSize = sizeof(RID_DEVICE_INFO); + + if (::GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_DEVICEINFO, &sRidDeviceInfo, &cbSize) == cbSize) { + if (TRACE_RIDI_DEVICEINFO == 1) { + switch (sRidDeviceInfo.dwType) { + case RIM_TYPEMOUSE: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEMOUSE\n"); + break; + case RIM_TYPEKEYBOARD: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEKEYBOARD\n"); + break; + case RIM_TYPEHID: + qDebug("\tsRidDeviceInfo.dwType=RIM_TYPEHID\n"); + qDebug("\tVendor=0x%x\n\tProduct=0x%x\n\tUsagePage=0x%x\n\tUsage=0x%x\n", + sRidDeviceInfo.hid.dwVendorId, + sRidDeviceInfo.hid.dwProductId, + sRidDeviceInfo.hid.usUsagePage, + sRidDeviceInfo.hid.usUsage); + break; + } + } + + if (sRidDeviceInfo.hid.dwVendorId == LOGITECH_VENDOR_ID) { + if (pRawInput->data.hid.bRawData[0] == 0x01) { // Translation vector + TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; + deviceData.fTimeToLive = kTimeToLive; + if (bIsForeground) { + short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + // Cache the pan zoom data + deviceData.fAxes[0] = static_cast(pnRawData[0]); + deviceData.fAxes[1] = static_cast(pnRawData[1]); + deviceData.fAxes[2] = static_cast(pnRawData[2]); + + //qDebug("Pan/Zoom RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[0], pnRawData[1], pnRawData[2]); + + if (pRawInput->data.hid.dwSizeHid >= 13) { // Highspeed package + // Cache the rotation data + deviceData.fAxes[3] = static_cast(pnRawData[3]); + deviceData.fAxes[4] = static_cast(pnRawData[4]); + deviceData.fAxes[5] = static_cast(pnRawData[5]); + deviceData.fIsDirty = true; + + //qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[3], pnRawData[4], pnRawData[5]); + return true; + } + } + else { // Zero out the data if the app is not in forground + deviceData.fAxes.assign(6, 0.f); + } + } + else if (pRawInput->data.hid.bRawData[0] == 0x02) { // Rotation vector + // If we are not in foreground do nothing + // The rotation vector was zeroed out with the translation vector in the previous message + if (bIsForeground) { + TInputData& deviceData = fDevice2Data[pRawInput->header.hDevice]; + deviceData.fTimeToLive = kTimeToLive; + + short* pnRawData = reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + // Cache the rotation data + deviceData.fAxes[3] = static_cast(pnRawData[0]); + deviceData.fAxes[4] = static_cast(pnRawData[1]); + deviceData.fAxes[5] = static_cast(pnRawData[2]); + deviceData.fIsDirty = true; + + //qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[0], pnRawData[1], pnRawData[2]); + + return true; + } + } + else if (pRawInput->data.hid.bRawData[0] == 0x03) { // Keystate change + // this is a package that contains 3d mouse keystate information + // bit0=key1, bit=key2 etc. + + unsigned long dwKeystate = *reinterpret_cast(&pRawInput->data.hid.bRawData[1]); + + //qDebug("ButtonData =0x%x\n", dwKeystate); + + // Log the keystate changes + unsigned long dwOldKeystate = fDevice2Keystate[pRawInput->header.hDevice]; + if (dwKeystate != 0) { + fDevice2Keystate[pRawInput->header.hDevice] = dwKeystate; + } + else { + fDevice2Keystate.erase(pRawInput->header.hDevice); + } + + // Only call the keystate change handlers if the app is in foreground + if (bIsForeground) { + unsigned long dwChange = dwKeystate ^ dwOldKeystate; + + for (int nKeycode = 1; nKeycode<33; nKeycode++) { + if (dwChange & 0x01) { + int nVirtualKeyCode = HidToVirtualKey(sRidDeviceInfo.hid.dwProductId, nKeycode); + if (nVirtualKeyCode) { + if (dwKeystate & 0x01) { + On3dmouseKeyDown(pRawInput->header.hDevice, nVirtualKeyCode); + } + else { + On3dmouseKeyUp(pRawInput->header.hDevice, nVirtualKeyCode); + } + } + } + dwChange >>= 1; + dwKeystate >>= 1; + } + } + } + } + } + return false; +} + +MouseParameters::MouseParameters() : +fNavigation(NAVIGATION_OBJECT_MODE), +fPivot(PIVOT_AUTO), +fPivotVisibility(PIVOT_SHOW), +fIsLockHorizon(true), +fIsPanZoom(true), +fIsRotate(true), +fSpeed(SPEED_LOW) +{ +} + +bool MouseParameters::IsPanZoom() const { + return fIsPanZoom; +} + +bool MouseParameters::IsRotate() const { + return fIsRotate; +} + +MouseParameters::Speed MouseParameters::GetSpeed() const { + return fSpeed; +} + +void MouseParameters::SetPanZoom(bool isPanZoom) { + fIsPanZoom = isPanZoom; +} + +void MouseParameters::SetRotate(bool isRotate) { + fIsRotate = isRotate; +} + +void MouseParameters::SetSpeed(Speed speed) { + fSpeed = speed; +} + +MouseParameters::Navigation MouseParameters::GetNavigationMode() const { + return fNavigation; +} + +MouseParameters::Pivot MouseParameters::GetPivotMode() const { + return fPivot; +} + +MouseParameters::PivotVisibility MouseParameters::GetPivotVisibility() const { + return fPivotVisibility; +} + +bool MouseParameters::IsLockHorizon() const { + return fIsLockHorizon; +} + +void MouseParameters::SetLockHorizon(bool bOn) { + fIsLockHorizon = bOn; +} + +void MouseParameters::SetNavigationMode(Navigation navigation) { + fNavigation = navigation; +} + +void MouseParameters::SetPivotMode(Pivot pivot) { + if (fPivot != PIVOT_MANUAL || pivot != PIVOT_AUTO_OVERRIDE) { + fPivot = pivot; + } +} + +void MouseParameters::SetPivotVisibility(PivotVisibility visibility) { + fPivotVisibility = visibility; +} + +#else + +int fConnexionClientID; + +static SpacemouseDeviceState lastState; + +static void DeviceAddedHandler(unsigned int connection); +static void DeviceRemovedHandler(unsigned int connection); +static void MessageHandler(unsigned int connection, unsigned int messageType, void *messageArgument); + +void SpacemouseManager::toggleSpacemouse(bool shouldEnable) { + if (shouldEnable && !Is3dmouseAttached()) { + init(); + } + if (!shouldEnable && Is3dmouseAttached()) { + destroy(); + } +} + +void SpacemouseManager::init() { + // Make sure the framework is installed + if (Menu::getInstance()->isOptionChecked(MenuOption::Connexion)) { + // Install message handler and register our client + InstallConnexionHandlers(MessageHandler, DeviceAddedHandler, DeviceRemovedHandler); + // Either use this to take over in our application only... does not work + // fConnexionClientID = RegisterConnexionClient('MCTt', "\pConnexion Client Test", kConnexionClientModeTakeOver, kConnexionMaskAll); + + // ...or use this to take over system-wide + fConnexionClientID = RegisterConnexionClient(kConnexionClientWildcard, NULL, kConnexionClientModeTakeOver, kConnexionMaskAll); + SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + memcpy(&spacemousedevice.clientId, &fConnexionClientID, (long)sizeof(int)); + + // A separate API call is required to capture buttons beyond the first 8 + SetConnexionClientButtonMask(fConnexionClientID, kConnexionMaskAllButtons); + + // use default switches + ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchesDisabled, NULL); + + if (Is3dmouseAttached() && spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(&spacemousedevice); + UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); + } + //let one axis be dominant + //ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchDominant | kConnexionSwitchEnableAll, NULL); + } +} + +void SpacemouseManager::destroy() { + // Make sure the framework is installed + if (&InstallConnexionHandlers != NULL) { + // Unregister our client and clean up all handlers + if (fConnexionClientID) { + UnregisterConnexionClient(fConnexionClientID); + } + CleanupConnexionHandlers(); + fConnexionClientID = 0; + SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + if (spacemousedevice.getDeviceID() != controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(spacemousedevice.getDeviceID()); + spacemousedevice.setDeviceID(0); + } + } +} + +void DeviceAddedHandler(unsigned int connection) { + SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + if (spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { + qCWarning(interfaceapp) << "Spacemouse device added "; + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(&spacemousedevice); + UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); + } +} + +void DeviceRemovedHandler(unsigned int connection) { + SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + if (spacemousedevice.getDeviceID() != controller::Input::INVALID_DEVICE) { + qCWarning(interfaceapp) << "Spacemouse device removed"; + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(spacemousedevice.getDeviceID()); + spacemousedevice.setDeviceID(controller::Input::INVALID_DEVICE); + } +} + +bool SpacemouseManager::Is3dmouseAttached() { + int result; + if (fConnexionClientID) { + if (ConnexionControl(kConnexionCtlGetDeviceID, 0, &result)) { + return false; + } + return true; + } + return false; +} + +void MessageHandler(unsigned int connection, unsigned int messageType, void *messageArgument) { + SpacemouseDeviceState *state; + + switch (messageType) { + case kConnexionMsgDeviceState: + state = (SpacemouseDeviceState*)messageArgument; + if (state->client == fConnexionClientID) { + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + instance->cc_position = { state->axis[0], state->axis[1], state->axis[2] }; + instance->cc_rotation = { state->axis[3], state->axis[4], state->axis[5] }; + + instance->handleAxisEvent(); + if (state->buttons != lastState.buttons) { + instance->setButton(state->buttons); + } + memmove(&lastState, state, (long)sizeof(SpacemouseDeviceState)); + } + break; + case kConnexionMsgPrefsChanged: + // the prefs have changed, do something + break; + default: + // other messageTypes can happen and should be ignored + break; + } + +} + +#endif // __APPLE__ + +#endif diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h new file mode 100644 index 0000000000..5379af9991 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -0,0 +1,236 @@ +// SpacemouseManager.h +// interface/src/devices +// +// Created by Marcel Verhagen on 09-06-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 +// + +#ifndef hifi_SpacemouseManager_h +#define hifi_SpacemouseManager_h + +#include +#include +#include +#include +#include + +#include "InputPlugin.h" + +#ifndef HAVE_SPACEMOUSE +class SpacemouseManager : public QObject { + Q_OBJECT +public: + static SpacemouseManager& getInstance(); + void ManagerFocusOutEvent(); + void init() {}; + void destroy() {}; + bool Is3dmouseAttached() { return false; }; + public slots: + void toggleConnexion(bool shouldEnable) {}; +}; +#endif + +#ifdef HAVE_SPACEMOUSE +// the windows connexion rawinput +#ifdef Q_OS_WIN + +#include "../../../interface/external/3dconnexionclient/include/I3dMouseParams.h" +#include +#include +#include +#include + +// windows rawinput parameters +class MouseParameters : public I3dMouseParam { +public: + MouseParameters(); + + // I3dmouseSensor interface + bool IsPanZoom() const; + bool IsRotate() const; + Speed GetSpeed() const; + + void SetPanZoom(bool isPanZoom); + void SetRotate(bool isRotate); + void SetSpeed(Speed speed); + + // I3dmouseNavigation interface + Navigation GetNavigationMode() const; + Pivot GetPivotMode() const; + PivotVisibility GetPivotVisibility() const; + bool IsLockHorizon() const; + + void SetLockHorizon(bool bOn); + void SetNavigationMode(Navigation navigation); + void SetPivotMode(Pivot pivot); + void SetPivotVisibility(PivotVisibility visibility); + + static bool Is3dmouseAttached(); + + + +private: + MouseParameters(const MouseParameters&); + const MouseParameters& operator = (const MouseParameters&); + + Navigation fNavigation; + Pivot fPivot; + PivotVisibility fPivotVisibility; + bool fIsLockHorizon; + + bool fIsPanZoom; + bool fIsRotate; + Speed fSpeed; +}; + +class SpacemouseManager : public QObject, public QAbstractNativeEventFilter { +//class SpacemouseManager : public InputPlugin, public controller::InputDevice { + + Q_OBJECT +public: + SpacemouseManager() {}; + + static SpacemouseManager& getInstance(); + void init(); + void destroy(); + bool Is3dmouseAttached(); + + SpacemouseManager* client; + + void ManagerFocusOutEvent(); + + I3dMouseParam& MouseParams(); + const I3dMouseParam& MouseParams() const; + + virtual void Move3d(HANDLE device, std::vector& motionData); + virtual void On3dmouseKeyDown(HANDLE device, int virtualKeyCode); + virtual void On3dmouseKeyUp(HANDLE device, int virtualKeyCode); + + virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) Q_DECL_OVERRIDE + { + MSG* msg = static_cast< MSG * >(message); + return RawInputEventFilter(message, result); + } + + public slots: + void toggleSpacemouse(bool shouldEnable); + + //std::shared_ptr getDevice(); + + +signals: + void Move3d(std::vector& motionData); + void On3dmouseKeyDown(int virtualKeyCode); + void On3dmouseKeyUp(int virtualKeyCode); + +private: + bool InitializeRawInput(HWND hwndTarget); + + bool RawInputEventFilter(void* msg, long* result); + + void OnRawInput(UINT nInputCode, HRAWINPUT hRawInput); + UINT GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT cbSizeHeader); + bool TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInput); + void On3dmouseInput(); + + class TInputData { + public: + TInputData() : fAxes(6) {} + + bool IsZero() { + return (0.0f == fAxes[0] && 0.0f == fAxes[1] && 0.0f == fAxes[2] && + 0.0f == fAxes[3] && 0.0f == fAxes[4] && 0.0f == fAxes[5]); + } + + int fTimeToLive; // For telling if the device was unplugged while sending data + bool fIsDirty; + std::vector fAxes; + + }; + + HWND fWindow; + + // Data cache to handle multiple rawinput devices + std::map< HANDLE, TInputData> fDevice2Data; + std::map< HANDLE, unsigned long> fDevice2Keystate; + + // 3dmouse parameters + MouseParameters f3dMouseParams; // Rotate, Pan Zoom etc. + + // use to calculate distance traveled since last event + DWORD fLast3dmouseInputTime; +}; + +// the osx connexion api +#else + +#include +#include "../../../interface/external/3dconnexionclient/include/ConnexionClientAPI.h" + +class SpacemouseManager : public QObject { + Q_OBJECT +public: + static SpacemouseManager& getInstance(); + void init(); + void destroy(); + bool Is3dmouseAttached(); + public slots: + void toggleSpacemouse(bool shouldEnable); +}; + +#endif // __APPLE__ + +#endif + + +// connnects to the userinputmapper +class SpacemouseDevice : public QObject, public controller::InputDevice { + Q_OBJECT + +public: + //static SpacemouseDevice& getInstance(); + SpacemouseDevice(); + enum PositionChannel { + TRANSLATE_X, + TRANSLATE_Y, + TRANSLATE_Z, + ROTATE_X, + ROTATE_Y, + ROTATE_Z, + }; + + enum ButtonChannel { + BUTTON_1 = 1, + BUTTON_2 = 2, + BUTTON_3 = 3 + }; + + typedef std::unordered_set ButtonPressedMap; + typedef std::map AxisStateMap; + + float getButton(int channel) const; + float getAxis(int channel) const; + + controller::Input makeInput(SpacemouseDevice::PositionChannel axis) const; + controller::Input makeInput(SpacemouseDevice::ButtonChannel button) const; + + controller::Input::NamedPair makePair(SpacemouseDevice::PositionChannel axis, const QString& name) const; + controller::Input::NamedPair makePair(SpacemouseDevice::ButtonChannel button, const QString& name) const; + + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + glm::vec3 cc_position; + glm::vec3 cc_rotation; + int clientId; + + void setButton(int lastButtonState); + void handleAxisEvent(); +}; + +#endif // defined(hifi_SpacemouseManager_h) \ No newline at end of file From eeabc9b093384731431df63c8efaaf30ef8ade57 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 23:49:03 -0800 Subject: [PATCH 0644/1003] Removing iviewhmd due to broken production builds --- interface/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fecdfe64fb..930bdbd7ce 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME interface) project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed -set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD") +set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK") if(WIN32) list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient") From 0ed868077d23b851aae8dbfb6b370df2f925e2e3 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 10:02:51 -0800 Subject: [PATCH 0645/1003] Added additive blending as option for particle effect --- .../RenderableParticleEffectEntityItem.cpp | 34 +++++++++++++++---- .../src/RenderableParticleEffectEntityItem.h | 6 ++-- .../entities/src/EntityItemProperties.cpp | 10 ++++++ libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityPropertyFlags.h | 1 + .../entities/src/ParticleEffectEntityItem.cpp | 11 +++++- .../entities/src/ParticleEffectEntityItem.h | 9 ++++- 7 files changed, 61 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index bc82159fbd..db0972a180 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -114,8 +114,7 @@ namespace render { } } -gpu::PipelinePointer RenderableParticleEffectEntityItem::_texturedPipeline; -gpu::PipelinePointer RenderableParticleEffectEntityItem::_untexturedPipeline; + EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -203,8 +202,17 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // sort particles back to front // NOTE: this is view frustum might be one frame out of date. + auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum(); + // No need to sort if we're doing additive blending + if (_additiveBlending != true) { + ::zSortAxis = frustum->getDirection(); + qSort(particleDetails.begin(), particleDetails.end(), zSort); + } + + + // allocate vertices _vertices.clear(); @@ -307,12 +315,21 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { } void RenderableParticleEffectEntityItem::createPipelines() { + bool writeToDepthBuffer = false; + gpu::State::BlendArg destinationColorBlendArg; + if (_additiveBlending) { + destinationColorBlendArg = gpu::State::ONE; + } + else { + destinationColorBlendArg = gpu::State::INV_SRC_ALPHA; + writeToDepthBuffer = true; + } if (!_untexturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, false, gpu::LESS_EQUAL); + state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - gpu::State::ONE, gpu::State::FACTOR_ALPHA, + destinationColorBlendArg, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(untextured_particle_vert))); auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(untextured_particle_frag))); @@ -322,13 +339,18 @@ void RenderableParticleEffectEntityItem::createPipelines() { if (!_texturedPipeline) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); - state->setDepthTest(true, false, gpu::LESS_EQUAL); + + + bool writeToDepthBuffer = !_additiveBlending; + qDebug() << "ADDITIVE BLENDING" << _additiveBlending; + state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, - gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, + destinationColorBlendArg, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert))); auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); + } } diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h index 5d69d19026..678f7eb904 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.h @@ -38,11 +38,11 @@ protected: uint32_t rgba; }; - static void createPipelines(); + void createPipelines(); std::vector _vertices; - static gpu::PipelinePointer _untexturedPipeline; - static gpu::PipelinePointer _texturedPipeline; + gpu::PipelinePointer _untexturedPipeline; + gpu::PipelinePointer _texturedPipeline; render::ScenePointer _scene; NetworkTexturePointer _texture; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 002f1bb527..4f5d256969 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -195,6 +195,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_ALPHA_SPREAD, alphaSpread); CHECK_PROPERTY_CHANGE(PROP_ALPHA_START, alphaStart); CHECK_PROPERTY_CHANGE(PROP_ALPHA_FINISH, alphaFinish); + CHECK_PROPERTY_CHANGE(PROP_ADDITIVE_BLENDING, additiveBlending); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL); CHECK_PROPERTY_CHANGE(PROP_VISIBLE, visible); @@ -351,6 +352,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ADDITIVE_BLENDING, additiveBlending); + } // Models only @@ -502,6 +505,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaFinish, float, setAlphaFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(additiveBlending, bool, setAdditiveBlending); COPY_PROPERTY_FROM_QSCRIPTVALUE(modelURL, QString, setModelURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(glowLevel, float, setGlowLevel); @@ -650,6 +654,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_FINISH, AlphaFinish, alphaFinish, float); + ADD_PROPERTY_TO_MAP(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); @@ -959,6 +964,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, properties.getAlphaSpread()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_START, properties.getAlphaStart()); APPEND_ENTITY_PROPERTY(PROP_ALPHA_FINISH, properties.getAlphaFinish()); + APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, properties.getAdditiveBlending()); } if (properties.getType() == EntityTypes::Zone) { @@ -1241,6 +1247,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); } if (properties.getType() == EntityTypes::Zone) { @@ -1579,6 +1586,9 @@ QList EntityItemProperties::listChangedProperties() { if (alphaFinishChanged()) { out += "alphaFinish"; } + if (additiveBlendingChanged()) { + out += "additiveBlending"; + } if (modelURLChanged()) { out += "modelURL"; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b95f4d35f4..84a5aeca5d 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -159,6 +159,7 @@ public: DEFINE_PROPERTY(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float, ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD); DEFINE_PROPERTY(PROP_RADIUS_START, RadiusStart, radiusStart, float, ParticleEffectEntityItem::DEFAULT_RADIUS_START); DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH); + DEFINE_PROPERTY(PROP_ADDITIVE_BLENDING, AdditiveBlending, additiveBlending, bool, ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 65060c8d45..b11d4d738b 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -142,6 +142,7 @@ enum EntityPropertyList { PROP_POLAR_FINISH, PROP_AZIMUTH_START, PROP_AZIMUTH_FINISH, + PROP_ADDITIVE_BLENDING, PROP_ANIMATION_LOOP, PROP_ANIMATION_FIRST_FRAME, diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index ddd79375b3..00ff0ed72a 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -94,6 +94,7 @@ const float ParticleEffectEntityItem::DEFAULT_RADIUS_SPREAD = 0.0f; const float ParticleEffectEntityItem::DEFAULT_RADIUS_START = DEFAULT_PARTICLE_RADIUS; const float ParticleEffectEntityItem::DEFAULT_RADIUS_FINISH = DEFAULT_PARTICLE_RADIUS; const QString ParticleEffectEntityItem::DEFAULT_TEXTURES = ""; +const bool ParticleEffectEntityItem::DEFAULT_ADDITIVE_BLENDING = false; EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -121,7 +122,8 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte _alphaMiddles(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), _alphaFinishes(DEFAULT_MAX_PARTICLES, DEFAULT_ALPHA), _particleMaxBound(glm::vec3(1.0f, 1.0f, 1.0f)), - _particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) + _particleMinBound(glm::vec3(-1.0f, -1.0f, -1.0f)) , + _additiveBlending(DEFAULT_ADDITIVE_BLENDING) { _type = EntityTypes::ParticleEffect; @@ -355,6 +357,8 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaStart, getAlphaStart); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alphaFinish, getAlphaFinish); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(additiveBlending, getAdditiveBlending); + return properties; } @@ -392,6 +396,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaStart, setAlphaStart); SET_ENTITY_PROPERTY_FROM_PROPERTIES(alphaFinish, setAlphaFinish); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(additiveBlending, setAdditiveBlending); if (somethingChanged) { bool wantDebug = false; @@ -435,6 +440,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch if (args.bitstreamVersion < VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) { // OLD PROP_EMIT_VELOCITY FAKEOUT SKIP_ENTITY_PROPERTY(PROP_EMIT_SPEED, glm::vec3); + SKIP_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool) } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_MODIFICATIONS) { @@ -481,6 +487,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); + READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); } return bytesRead; @@ -520,6 +527,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea requestedProperties += PROP_POLAR_FINISH; requestedProperties += PROP_AZIMUTH_START; requestedProperties += PROP_AZIMUTH_FINISH; + requestedProperties += PROP_ADDITIVE_BLENDING; return requestedProperties; } @@ -562,6 +570,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, APPEND_ENTITY_PROPERTY(PROP_POLAR_FINISH, getPolarFinish()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_START, getAzimuthStart()); APPEND_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, getAzimuthFinish()); + APPEND_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, getAdditiveBlending()); } bool ParticleEffectEntityItem::isEmittingParticles() const { diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 029d65cfc0..6560a7bc33 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -209,6 +209,12 @@ public: } } + static const bool DEFAULT_ADDITIVE_BLENDING; + bool getAdditiveBlending() const { return _additiveBlending; } + void setAdditiveBlending(bool additiveBlending) { + _additiveBlending = additiveBlending; + } + protected: bool isAnimatingSomething() const; @@ -219,7 +225,6 @@ protected: void extendBounds(const glm::vec3& point); void integrateParticle(quint32 index, float deltaTime); quint32 getLivingParticleCount() const; - // the properties of this entity rgbColor _color; xColor _colorStart = DEFAULT_COLOR; @@ -284,6 +289,8 @@ protected: // bounding volume glm::vec3 _particleMaxBound; glm::vec3 _particleMinBound; + + bool _additiveBlending; }; #endif // hifi_ParticleEffectEntityItem_h From 27b239176f32cc82b9dfc95baa07a11050800ef1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 3 Nov 2015 10:16:56 -0800 Subject: [PATCH 0646/1003] fix grab --- examples/controllers/handControllerGrab.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 30c99f43f8..edb9fe05bd 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -369,7 +369,7 @@ function MyController(hand) { disabledHand = 'none'; } - if (!grabbableData.grabbable) { + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { this.grabbedEntity = null; continue; } @@ -379,7 +379,6 @@ function MyController(hand) { } if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); } else { From 73d7fd4648208cf8c201105129b009ed55164ddb Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 3 Nov 2015 19:19:12 +0100 Subject: [PATCH 0647/1003] Fix: Vec3.UP was pointed to Vectors::UNIT_X This fixes the scripting property Vec3.UP to point to Vectors::UP which is equal to {X: 0, Y: 1, Z: 0}. --- libraries/script-engine/src/Vec3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 17a5afd09a..71b6cf3872 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -84,7 +84,7 @@ private: const glm::vec3& TWO() { return Vectors::TWO; } const glm::vec3& HALF() { return Vectors::HALF; } const glm::vec3& RIGHT() { return Vectors::RIGHT; } - const glm::vec3& UP() { return Vectors::UNIT_X; } + const glm::vec3& UP() { return Vectors::UP; } const glm::vec3& FRONT() { return Vectors::FRONT; } }; From 811fd0cec7f786011dfe23bdf57634025ff21220 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 3 Nov 2015 10:42:15 -0800 Subject: [PATCH 0648/1003] prep for meeting --- examples/example/games/color_busters/colorBusterWand.js | 2 +- examples/example/games/color_busters/createColorBusterCubes.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example/games/color_busters/colorBusterWand.js b/examples/example/games/color_busters/colorBusterWand.js index 6080d6f345..f9d69e8414 100644 --- a/examples/example/games/color_busters/colorBusterWand.js +++ b/examples/example/games/color_busters/colorBusterWand.js @@ -247,7 +247,7 @@ var position = Entities.getEntityProperties(this.entityID, "position").position; var audioProperties = { - volume: 0.5, + volume: 0.25, position: position }; diff --git a/examples/example/games/color_busters/createColorBusterCubes.js b/examples/example/games/color_busters/createColorBusterCubes.js index 6a2942bbe9..3fdd772704 100644 --- a/examples/example/games/color_busters/createColorBusterCubes.js +++ b/examples/example/games/color_busters/createColorBusterCubes.js @@ -19,7 +19,7 @@ var CUBE_DIMENSIONS = { z: 1 }; -var NUMBER_OF_CUBES_PER_SIDE = 5; +var NUMBER_OF_CUBES_PER_SIDE = 8; var STARTING_CORNER_POSITION = { x: 100, From c139b62217742ce96ee43c712db6e5c40f0cc012 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 3 Nov 2015 11:19:49 -0800 Subject: [PATCH 0649/1003] fps calculation now based on moving average fps stat is now calcualted the same way as simRate and avatarRate are calculated, based on a moving average that is sampled once a second. Also, Removed unused constants and renamed Application::checkFPS to Application::ping --- interface/src/Application.cpp | 39 +++++++++++++++++++++-------------- interface/src/Application.h | 4 +++- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4c55e05b01..1f04338f99 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -159,7 +159,7 @@ static QTimer locationUpdateTimer; static QTimer balanceUpdateTimer; static QTimer identityPacketTimer; static QTimer billboardPacketTimer; -static QTimer checkFPStimer; +static QTimer pingTimer; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SVO_EXTENSION = ".svo"; @@ -183,9 +183,7 @@ static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS static const QString INFO_HELP_PATH = "html/interface-welcome.html"; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; -static const unsigned int TARGET_SIM_FRAMERATE = 60; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; -static const int TARGET_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / TARGET_SIM_FRAMERATE; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; #ifndef __APPLE__ @@ -842,7 +840,7 @@ void Application::cleanupBeforeQuit() { balanceUpdateTimer.stop(); identityPacketTimer.stop(); billboardPacketTimer.stop(); - checkFPStimer.stop(); + pingTimer.stop(); QMetaObject::invokeMethod(&_settingsTimer, "stop", Qt::BlockingQueuedConnection); // save state @@ -977,8 +975,8 @@ void Application::initializeGL() { _entityEditSender.initialize(_enableProcessOctreeThread); // call our timer function every second - connect(&checkFPStimer, &QTimer::timeout, this, &Application::checkFPS); - checkFPStimer.start(1000); + connect(&pingTimer, &QTimer::timeout, this, &Application::ping); + pingTimer.start(1000); _idleLoopStdev.reset(); @@ -1312,7 +1310,6 @@ void Application::paintGL() { { PerformanceTimer perfTimer("makeCurrent"); _offscreenContext->makeCurrent(); - _frameCount++; Stats::getInstance()->setRenderDetails(renderArgs._details); // Reset the gpu::Context Stages @@ -1321,6 +1318,24 @@ void Application::paintGL() { batch.resetStages(); }); } + + // update fps moving average + { + uint64_t now = usecTimestampNow(); + static uint64_t lastPaintEnd{ now }; + uint64_t diff = now - lastPaintEnd; + if (diff != 0) { + _framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff); + } + lastPaintEnd = now; + + // update fps once a second + if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) { + _fps = _framesPerSecond.getAverage(); + _lastFramesPerSecondUpdate = now; + } + _frameCount++; + } } void Application::runTests() { @@ -2085,17 +2100,11 @@ bool Application::acceptSnapshot(const QString& urlString) { return true; } -// Every second, check the frame rates and other stuff -void Application::checkFPS() { +// Every second, send a ping, if menu item is checked. +void Application::ping() { if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { DependencyManager::get()->sendPingPackets(); } - - float diffTime = (float)_timerStart.nsecsElapsed() / 1000000000.0f; - - _fps = (float)_frameCount / diffTime; - _frameCount = 0; - _timerStart.start(); } void Application::idle() { diff --git a/interface/src/Application.h b/interface/src/Application.h index dfc904e0ef..0fd8c74494 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -306,7 +306,7 @@ public slots: private slots: void clearDomainOctreeDetails(); - void checkFPS(); + void ping(); void idle(); void aboutToQuit(); @@ -541,6 +541,8 @@ private: EntityItemID _keyboardFocusedItem; quint64 _lastAcceptedKeyPress = 0; + SimpleMovingAverage _framesPerSecond{10}; + quint64 _lastFramesPerSecondUpdate = 0; SimpleMovingAverage _simsPerSecond{10}; int _simsPerSecondReport = 0; quint64 _lastSimsPerSecondUpdate = 0; From 6bf9a4518aecc97adbce448eee4ae3f573179bca Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Nov 2015 10:36:25 -0800 Subject: [PATCH 0650/1003] Bail early from ScriptEngine::run if stopped evaluate() bails anyway, so this will avoid the cost of init(). If run() is invoked from runInThread(), this may avoid a race where _isRunning is set after it is checked because the check occured during init(). --- libraries/script-engine/src/ScriptEngine.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c6ac6495b1..381eef63db 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -646,13 +646,14 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi } void ScriptEngine::run() { - // TODO: can we add a short circuit for _stoppingAllScripts here? What does it mean to not start running if - // we're in the process of stopping? - + if (_stoppingAllScripts) { + return; // bail early - avoid setting state in init(), as evaluate() will bail too + } + if (!_isInitialized) { init(); } - + _isRunning = true; _isFinished = false; if (_wantSignals) { From 12ff9734e05391cfe3124cd3f24c6fe4a1ffbf8c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 3 Nov 2015 11:26:49 -0800 Subject: [PATCH 0651/1003] init _bodySensorMatrix in ctor --- interface/src/avatar/MyAvatar.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 00870c62c6..4376aa6d79 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -121,6 +121,12 @@ MyAvatar::MyAvatar(RigPointer rig) : connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation); _characterController.setEnabled(true); + + _hmdSensorMatrix = glm::mat4(); + _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); + _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); + _bodySensorMatrix = deriveBodyFromHMDSensor(); + updateSensorToWorldMatrix(); } MyAvatar::~MyAvatar() { From 4c076d8b128d1984dca520ec5c8bba28ca56a333 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 3 Nov 2015 11:34:05 -0800 Subject: [PATCH 0652/1003] Disabling billboard rendering, source of the evil black rectangles --- interface/src/avatar/Avatar.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 675a71a2f4..b979334383 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -661,6 +661,9 @@ void Avatar::updateJointMappings() { } void Avatar::renderBillboard(RenderArgs* renderArgs) { + // FIXME disabling the billboard because it doesn't appear to work reliably + // the billboard is ending up with a random texture and position. + return; if (_billboard.isEmpty()) { return; } From e160e2a7ae88a1d7ca632fa6c15ad69196811987 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 3 Nov 2015 11:37:30 -0800 Subject: [PATCH 0653/1003] the minimal fix --- interface/src/avatar/MyAvatar.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4376aa6d79..a5b39ef6e9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -122,11 +122,7 @@ MyAvatar::MyAvatar(RigPointer rig) : this, &MyAvatar::goToLocation); _characterController.setEnabled(true); - _hmdSensorMatrix = glm::mat4(); - _hmdSensorPosition = extractTranslation(_hmdSensorMatrix); - _hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix); _bodySensorMatrix = deriveBodyFromHMDSensor(); - updateSensorToWorldMatrix(); } MyAvatar::~MyAvatar() { From 82dbdec78c074b8eb6b5690edfc349b61407fb3e Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 11:54:01 -0800 Subject: [PATCH 0654/1003] helicopter --- examples/helicopter/helicopter.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/helicopter/helicopter.js diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js new file mode 100644 index 0000000000..3ce084c305 --- /dev/null +++ b/examples/helicopter/helicopter.js @@ -0,0 +1,16 @@ +var modelURL= "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx"; +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + +var helicopter = Entities.addEntity({ + type: "Model", + modelURL: modelURL, + position: center +}); + + + +function cleanup() { + Entities.deleteEntity(helicopter); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 443df042a4b050a18a64934a28b491c53bba6128 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 12:40:41 -0800 Subject: [PATCH 0655/1003] playing with light pos/rot --- examples/helicopter/helicopter.js | 50 ++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index 3ce084c305..7631f166f9 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -1,16 +1,64 @@ -var modelURL= "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx"; +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx"; var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); +// These constants define the Spotlight position and orientation relative to the model +var MODEL_LIGHT_POSITION = { + x: -2, + y: 0, + z: 0 +}; +var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 0, + y: 1, + z: 0 +}); + +// Evaluate the world light entity positions and orientations from the model ones +function evalLightWorldTransform(modelPos, modelRot) { + + return { + p: Vec3.sum(modelPos, Vec3.multiplyQbyV(modelRot, MODEL_LIGHT_POSITION)), + q: Quat.multiply(modelRot, MODEL_LIGHT_ROTATION) + }; +} + + var helicopter = Entities.addEntity({ type: "Model", + name: "Helicopter", modelURL: modelURL, position: center }); +var modelProperties = Entities.getEntityProperties(helicopter, ['position', 'rotation']); +var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); +var spotlight = Entities.addEntity({ + type: "Light", + name: "helicopter light", + position: lightTransform.p, + rotation: lightTransform.q, + intensity: 2, + color: {red: 200, green: 200, blue: 255}, + intensity: 2, + exponent: 0.3, + cutoff: 20 +}); + +var debugLight = Entities.addEntity({ + type: "Box", + position: lightTransform.p, + rotation: lightTransform.q, + dimensions: {x: 4, y: 1, z: 1}, + color: {red: 200, green: 200, blue: 0} +}); + function cleanup() { + Entities.deleteEntity(debugLight); Entities.deleteEntity(helicopter); + Entities.deleteEntity(spotlight); + } Script.scriptEnding.connect(cleanup); \ No newline at end of file From 777fbe6d834ce8bbdd5f810a42714a7d5af850bc Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 12:43:41 -0800 Subject: [PATCH 0656/1003] moved additive_blending flag to proper location --- libraries/entities/src/EntityPropertyFlags.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index b11d4d738b..e5fa2983e2 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -142,7 +142,6 @@ enum EntityPropertyList { PROP_POLAR_FINISH, PROP_AZIMUTH_START, PROP_AZIMUTH_FINISH, - PROP_ADDITIVE_BLENDING, PROP_ANIMATION_LOOP, PROP_ANIMATION_FIRST_FRAME, @@ -150,6 +149,8 @@ enum EntityPropertyList { PROP_ANIMATION_HOLD, PROP_ANIMATION_START_AUTOMATICALLY, + PROP_ADDITIVE_BLENDING, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, From a7162eefbebbc550548f9eba209331643a7c0deb Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 3 Nov 2015 13:10:25 -0800 Subject: [PATCH 0657/1003] Add alphaVar for ik. --- .../resources/meshes/defaultAvatar_full/avatar-animation.json | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 9398b94890..df8b9b16c8 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -5,6 +5,7 @@ "type": "overlay", "data": { "alpha": 1.0, + "alphaVar": "ikOverlayAlpha", "boneSet": "fullBody" }, "children": [ From fb4174b9a2753fbf280c6e59520264e360589bc5 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 3 Nov 2015 13:11:31 -0800 Subject: [PATCH 0658/1003] Control alphaVar, and deal with overlapping cycles. --- examples/away.js | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/examples/away.js b/examples/away.js index 9fd688dbb7..c183f236fd 100644 --- a/examples/away.js +++ b/examples/away.js @@ -5,6 +5,7 @@ // Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. // See MAIN CONTROL, below, for what "paused" actually does. +var IK_WINDOW_AFTER_GOING_ACTIVE = 3000; // milliseconds var OVERLAY_DATA = { text: "Paused:\npress any key to continue", font: {size: 75}, @@ -15,23 +16,41 @@ var OVERLAY_DATA = { // ANIMATION // We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect // using an animation graph with a state that we turn on and off through the animation var defined with that state. -var awayAnimationHandlerId, activeAnimationHandlerId; +var awayAnimationHandlerId, activeAnimationHandlerId, stopper; function playAwayAnimation() { function animateAway() { - return {isAway: true, isNotAway: false, isNotMoving: false}; + return {isAway: true, isNotAway: false, isNotMoving: false, ikOverlayAlpha: 0.0}; + } + if (stopper) { + Script.clearTimeout(stopper); + MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment } awayAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateAway, null); } function stopAwayAnimation() { MyAvatar.removeAnimationStateHandler(awayAnimationHandlerId); + if (stopper) { print('WARNING: unexpected double stop'); return; } + // How do we know when to turn ikOverlayAlpha back on? + // It cannot be as soon as we want to stop the away animation, because then things will look goofy as we come out of that animation. + // (Imagine an away animation that sits or kneels, and then stands back up when coming out of it. If head is at the HMD, then it won't + // want to track the standing up animation.) + // Our standard anim graph flips 'awayOutroOnDone' for one frame, but it's a trigger (not an animVar) and other folks might use different graphs. + // So... Just give us a fixed amount of time to be done with animation, before we turn ik back on. + var backToNormal = false; + stopper = Script.setTimeout(function () { + backToNormal = true; + stopper = false; + }, IK_WINDOW_AFTER_GOING_ACTIVE); function animateActive(state) { - if (!state.isAway) { // Once the right state gets reflected back to us, we don't need the hander any more. - // But we are locked against handler changes during the execution of a handler, so remove asynchronously. - Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0); - } - return {isAway: false, isNotAway: true}; // IWBNI we had a way of deleting an anim var. + if (state.ikOverlayAlpha) { + // Once the right state gets reflected back to us, we don't need the hander any more. + // But we are locked against handler changes during the execution of a handler, so remove asynchronously. + Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0); + } + // It might be cool to "come back to life" by fading the ik overlay back in over a short time. But let's see how this goes. + return {isAway: false, isNotAway: true, ikOverlayAlpha: backToNormal ? 1.0 : 0.0}; // IWBNI we had a way of deleting an anim var. } - activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['isAway']); + activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['isAway', 'isNotAway', 'isNotMoving', 'ikOverlayAlpha']); } // OVERLAY @@ -48,8 +67,8 @@ hideOverlay(); // MAIN CONTROL var wasMuted, isAway; -function goAway() { - if (isAway) { +function goAway(event) { + if (isAway || event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) return; } isAway = true; @@ -63,9 +82,9 @@ function goAway() { showOverlay(); } function goActive(event) { - if (!isAway) { + if (!isAway || event.isAutoRepeat) { if (event.text === '.') { - goAway(); + goAway(event); } return; } @@ -79,6 +98,7 @@ function goActive(event) { hideOverlay(); } Controller.keyPressEvent.connect(goActive); +Script.scriptEnding.connect(goActive); if (HMD.active) { - goAway(); + goAway({}); // give a dummy event object } From 58587a5780aa6eba447af94f987690aed3c6165b Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 13:26:39 -0800 Subject: [PATCH 0659/1003] spotlight --- examples/helicopter/helicopter.js | 70 ++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index 7631f166f9..22df9e56b7 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -1,16 +1,16 @@ -var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx"; -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx?v3"; +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); // These constants define the Spotlight position and orientation relative to the model var MODEL_LIGHT_POSITION = { - x: -2, + x: 2, y: 0, - z: 0 + z: -5 }; -var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { +var MODEL_LIGHT_ROTATION = Quat.angleAxis(0, { x: 0, - y: 1, - z: 0 + y: 0, + z: 1 }); // Evaluate the world light entity positions and orientations from the model ones @@ -27,31 +27,46 @@ var helicopter = Entities.addEntity({ type: "Model", name: "Helicopter", modelURL: modelURL, - position: center + dimensions: {x: 12.13, y: 3.14, z: 9.92}, + // rotation: Quat.fromPitchYawRollDegrees(0, -90, 0), + position: center, + collisionsWillMove: true }); -var modelProperties = Entities.getEntityProperties(helicopter, ['position', 'rotation']); -var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); var spotlight = Entities.addEntity({ type: "Light", name: "helicopter light", - position: lightTransform.p, - rotation: lightTransform.q, intensity: 2, - color: {red: 200, green: 200, blue: 255}, - intensity: 2, - exponent: 0.3, - cutoff: 20 + color: { + red: 200, + green: 200, + blue: 255 + }, + intensity: 10, + dimensions: { + x: 2, + y: 2, + z: 200 + }, + exponent: .1, + cutoff: 10, + isSpotlight: true }); var debugLight = Entities.addEntity({ type: "Box", - position: lightTransform.p, - rotation: lightTransform.q, - dimensions: {x: 4, y: 1, z: 1}, - color: {red: 200, green: 200, blue: 0} + dimensions: { + x: .1, + y: .1, + z: .3 + }, + color: { + red: 200, + green: 200, + blue: 0 + } }); function cleanup() { @@ -61,4 +76,19 @@ function cleanup() { } +function update() { + var modelProperties = Entities.getEntityProperties(helicopter, ['position', 'rotation']); + var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); + Entities.editEntity(spotlight, { + position: lightTransform.p, + // rotation: lightTransform.q + }); + Entities.editEntity(debugLight, { + position: lightTransform.p, + rotation: lightTransform.q + }); +} + + +Script.update.connect(update); Script.scriptEnding.connect(cleanup); \ No newline at end of file From b83b24b096a35d217d944844229405f6bf64628c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 3 Nov 2015 13:42:18 -0800 Subject: [PATCH 0660/1003] remove strange test file --- examples/tests/timerStressTest.js | 68 ------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 examples/tests/timerStressTest.js diff --git a/examples/tests/timerStressTest.js b/examples/tests/timerStressTest.js deleted file mode 100644 index a1cf76c5a6..0000000000 --- a/examples/tests/timerStressTest.js +++ /dev/null @@ -1,68 +0,0 @@ -// createBoxes.js -// part of bubblewand -// -// Created by James B. Pollack @imgntn -- 09/07/2015 -// Copyright 2015 High Fidelity, Inc. -// -// Loads a wand model and attaches the bubble wand behavior. -// 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("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/utilities.js"); -Script.include("https://raw.githubusercontent.com/highfidelity/hifi/master/examples/libraries/utils.js"); - -var bubbleModel = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx?' + randInt(0, 10000);; -//var scriptURL'http://hifi-public.s3.amazonaws.com/james/bubblewand/scripts/wand.js?'+randInt(0,10000); - -//for local testing -//var scriptURL = "http://localhost:8080/scripts/setRecurringTimeout.js?" + randInt(0, 10000); - - -var scriptURL='http://hifi-public.s3.amazonaws.com/james/debug/timeouts/setRecurringTimeout.js?'+ randInt(0, 10000); -//create the wand in front of the avatar - -var boxes=[]; -var TEST_ENTITY_NAME = "TimerScript"; - - -var TOTAL_ENTITIES = 100; -for (var i = 0; i < TOTAL_ENTITIES; i++) { - var box = Entities.addEntity({ - type: "Box", - name: TEST_ENTITY_NAME, - position: { - x: randInt(0, 100) - 50 + MyAvatar.position.x, - y: randInt(0, 100) - 50 + MyAvatar.position.x, - z: randInt(0, 100) - 50 + MyAvatar.position.x, - }, - dimensions: { - x: 1, - y: 1, - z: 1, - }, - color: { - red: 255, - green: 0, - blue: 0, - }, - //must be enabled to be grabbable in the physics engine - collisionsWillMove: true, - shapeType: 'box', - lifetime:60, - script: scriptURL - }); - boxes.push(box) -} - - -function cleanup() { - while (boxes.length > 0) { - Entities.deleteEntity(boxes.pop()); - - } -} - - -Script.scriptEnding.connect(cleanup); \ No newline at end of file From d544b7a645a779d95caed858b36b1440a5f9dc5a Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Tue, 3 Nov 2015 13:59:15 -0800 Subject: [PATCH 0661/1003] fix bug: avatar can't walking when HMD at rest --- interface/src/avatar/MyAvatar.cpp | 37 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a5b39ef6e9..741a82fdb8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -347,23 +347,6 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } void MyAvatar::updateHMDFollowVelocity() { - bool isMoving; - if (_lastIsMoving) { - const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec - isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; - } else { - const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec - isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; - } - - bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; - _lastIsMoving = isMoving; - - bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); - if (hmdIsAtRest || justStartedMoving) { - _isFollowingHMD = true; - } - // compute offset to body's target position (in sensor-frame) auto sensorBodyMatrix = deriveBodyFromHMDSensor(); _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); @@ -372,13 +355,29 @@ void MyAvatar::updateHMDFollowVelocity() { // don't pull the body DOWN to match the target (allow animation system to squat) truncatedOffset.y = 0.0f; } + float truncatedOffsetDistance = glm::length(truncatedOffset); + + bool isMoving; + if (_lastIsMoving) { + const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec + isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; + } else { + const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec + isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; + } + bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; + _lastIsMoving = isMoving; + bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); + const float MIN_HMD_HIP_SHIFT = 0.05f; + if (justStartedMoving || (hmdIsAtRest && truncatedOffsetDistance > MIN_HMD_HIP_SHIFT)) { + _isFollowingHMD = true; + } bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); if (!needNewFollowSpeed) { // check to see if offset has exceeded its threshold - float distance = glm::length(truncatedOffset); const float MAX_HMD_HIP_SHIFT = 0.2f; - if (distance > MAX_HMD_HIP_SHIFT) { + if (truncatedOffsetDistance > MAX_HMD_HIP_SHIFT) { _isFollowingHMD = true; needNewFollowSpeed = true; } From 0846c3bef4702177969837bab9867669ec173927 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 14:12:56 -0800 Subject: [PATCH 0662/1003] moving --- examples/helicopter/helicopter.js | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index 22df9e56b7..748d1d62c3 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -1,5 +1,13 @@ var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx?v3"; -var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(Camera.getOrientation()))); +var spawnPosition = {x: 1031, y: 135, z: 1041}; + +var speed = .1; + +var helicopterSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/ryan/helicopter.L.wav"); +var audioInjector = Audio.playSound(helicopterSound, { + volume: 1, + loop: true +}); // These constants define the Spotlight position and orientation relative to the model var MODEL_LIGHT_POSITION = { @@ -29,8 +37,7 @@ var helicopter = Entities.addEntity({ modelURL: modelURL, dimensions: {x: 12.13, y: 3.14, z: 9.92}, // rotation: Quat.fromPitchYawRollDegrees(0, -90, 0), - position: center, - collisionsWillMove: true + position: spawnPosition, }); @@ -50,8 +57,8 @@ var spotlight = Entities.addEntity({ y: 2, z: 200 }, - exponent: .1, - cutoff: 10, + exponent: 1, + cutoff: 40, isSpotlight: true }); @@ -87,6 +94,18 @@ function update() { position: lightTransform.p, rotation: lightTransform.q }); + + audioInjector.setOptions({ + position: modelProperties.position, + }); + + //Move forward + var newRotation = Quat.multiply(modelProperties.rotation, {x: 0, y: .001, z: 0, w: 1}) + var newPosition = Vec3.sum(modelProperties.position, Vec3.multiply(speed, Quat.getFront(modelProperties.rotation))); + Entities.editEntity(helicopter, { + position: newPosition, + rotation: newRotation + }) } From 8346ce27b322aa2d422045b6a4a97d7a0d309ab3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 3 Nov 2015 14:22:41 -0800 Subject: [PATCH 0663/1003] allow a non-colliding grab of locked entities --- examples/controllers/handControllerGrab.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index edb9fe05bd..936a00db07 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -291,7 +291,7 @@ function MyController(hand) { return this.triggerValue < TRIGGER_OFF_VALUE; }; - this.triggerSqueezed = function() { + this.triggerSqueezed = function() { var triggerValue = this.rawTriggerValue; return triggerValue > TRIGGER_ON_VALUE; }; @@ -352,7 +352,7 @@ function MyController(hand) { var intersection = Entities.findRayIntersection(pickRayBacked, true); - if (intersection.intersects && !intersection.properties.locked) { + if (intersection.intersects) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); this.grabbedEntity = intersection.entityID; @@ -381,7 +381,7 @@ function MyController(hand) { // the hand is very close to the intersected object. go into close-grabbing mode. if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } else { + } else if (!intersection.properties.locked) { this.setState(STATE_NEAR_GRABBING); } } else { @@ -390,7 +390,8 @@ function MyController(hand) { this.grabbedEntity = null; } else { // the hand is far from the intersected object. go into distance-holding mode - if (intersection.properties.collisionsWillMove) { + if (intersection.properties.collisionsWillMove + && !intersection.properties.locked) { this.setState(STATE_DISTANCE_HOLDING); } else { this.setState(STATE_FAR_GRABBING_NON_COLLIDING); @@ -502,7 +503,7 @@ function MyController(hand) { // How far did the avatar turn this timestep? // Note: The following code is too long because we need a Quat.quatBetween() function - // that returns the minimum quaternion between two quaternions. + // that returns the minimum quaternion between two quaternions. var currentOrientation = MyAvatar.orientation; if (Quat.dot(currentOrientation, this.currentAvatarOrientation) < 0.0) { var negativeCurrentOrientation = { From ed2bd075859c20c48c1942ec215aa93fed930153 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 14:54:05 -0800 Subject: [PATCH 0664/1003] rotating blade --- examples/helicopter/helicopter.js | 50 +++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index 748d1d62c3..154613f20c 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -1,5 +1,10 @@ var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx?v3"; -var spawnPosition = {x: 1031, y: 135, z: 1041}; +var animationURL = "https://s3.amazonaws.com/hifi-public/eric/models/bladeAnimation.fbx?v7"; +var spawnPosition = { + x: 1031, + y: 135, + z: 1041 +}; var speed = .1; @@ -15,7 +20,7 @@ var MODEL_LIGHT_POSITION = { y: 0, z: -5 }; -var MODEL_LIGHT_ROTATION = Quat.angleAxis(0, { +var MODEL_LIGHT_ROTATION = Quat.angleAxis(0, { x: 0, y: 0, z: 1 @@ -35,7 +40,17 @@ var helicopter = Entities.addEntity({ type: "Model", name: "Helicopter", modelURL: modelURL, - dimensions: {x: 12.13, y: 3.14, z: 9.92}, + animation: { + url: animationURL, + running: true, + fps: 180 + + }, + dimensions: { + x: 12.13, + y: 3.14, + z: 9.92 + }, // rotation: Quat.fromPitchYawRollDegrees(0, -90, 0), position: spawnPosition, }); @@ -87,25 +102,30 @@ function update() { var modelProperties = Entities.getEntityProperties(helicopter, ['position', 'rotation']); var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); Entities.editEntity(spotlight, { - position: lightTransform.p, - // rotation: lightTransform.q + position: lightTransform.p, + // rotation: lightTransform.q }); - Entities.editEntity(debugLight, { - position: lightTransform.p, - rotation: lightTransform.q + Entities.editEntity(debugLight, { + position: lightTransform.p, + rotation: lightTransform.q }); - audioInjector.setOptions({ + audioInjector.setOptions({ position: modelProperties.position, - }); + }); - //Move forward - var newRotation = Quat.multiply(modelProperties.rotation, {x: 0, y: .001, z: 0, w: 1}) - var newPosition = Vec3.sum(modelProperties.position, Vec3.multiply(speed, Quat.getFront(modelProperties.rotation))); - Entities.editEntity(helicopter, { + //Move forward + var newRotation = Quat.multiply(modelProperties.rotation, { + x: 0, + y: .001, + z: 0, + w: 1 + }) + var newPosition = Vec3.sum(modelProperties.position, Vec3.multiply(speed, Quat.getFront(modelProperties.rotation))); + Entities.editEntity(helicopter, { position: newPosition, rotation: newRotation - }) + }) } From 0650280fb665f60a54858a3831d75ab35b27ee46 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 15:14:56 -0800 Subject: [PATCH 0665/1003] tweaks --- examples/helicopter/helicopter.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index 154613f20c..44482e1c85 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -2,15 +2,15 @@ var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/helicopter.fbx? var animationURL = "https://s3.amazonaws.com/hifi-public/eric/models/bladeAnimation.fbx?v7"; var spawnPosition = { x: 1031, - y: 135, + y: 145, z: 1041 }; -var speed = .1; +var speed = .15; var helicopterSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/ryan/helicopter.L.wav"); var audioInjector = Audio.playSound(helicopterSound, { - volume: 1, + volume: 0.3, loop: true }); @@ -20,10 +20,10 @@ var MODEL_LIGHT_POSITION = { y: 0, z: -5 }; -var MODEL_LIGHT_ROTATION = Quat.angleAxis(0, { - x: 0, +var MODEL_LIGHT_ROTATION = Quat.angleAxis(-90, { + x: 1, y: 0, - z: 1 + z: 0 }); // Evaluate the world light entity positions and orientations from the model ones @@ -66,14 +66,14 @@ var spotlight = Entities.addEntity({ green: 200, blue: 255 }, - intensity: 10, + intensity: 1, dimensions: { x: 2, y: 2, z: 200 }, - exponent: 1, - cutoff: 40, + exponent: 0.01, + cutoff: 10, isSpotlight: true }); @@ -103,7 +103,7 @@ function update() { var lightTransform = evalLightWorldTransform(modelProperties.position, modelProperties.rotation); Entities.editEntity(spotlight, { position: lightTransform.p, - // rotation: lightTransform.q + rotation: lightTransform.q }); Entities.editEntity(debugLight, { position: lightTransform.p, @@ -117,7 +117,7 @@ function update() { //Move forward var newRotation = Quat.multiply(modelProperties.rotation, { x: 0, - y: .001, + y: .002, z: 0, w: 1 }) From 4d62c532f57eb0ea974c92bb0016ae56a2b5d011 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 3 Nov 2015 15:35:13 -0800 Subject: [PATCH 0666/1003] Removing dead config files --- .../resources/controllers/mapping-config.json | 24 ----------- .../resources/controllers/mapping-test0.json | 36 ---------------- .../resources/controllers/standard-old.json | 43 ------------------- 3 files changed, 103 deletions(-) delete mode 100644 interface/resources/controllers/mapping-config.json delete mode 100644 interface/resources/controllers/mapping-test0.json delete mode 100644 interface/resources/controllers/standard-old.json diff --git a/interface/resources/controllers/mapping-config.json b/interface/resources/controllers/mapping-config.json deleted file mode 100644 index 2ccd216c2f..0000000000 --- a/interface/resources/controllers/mapping-config.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "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 deleted file mode 100644 index 5232c97f19..0000000000 --- a/interface/resources/controllers/mapping-test0.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "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/controllers/standard-old.json b/interface/resources/controllers/standard-old.json deleted file mode 100644 index b662e5394d..0000000000 --- a/interface/resources/controllers/standard-old.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "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" } - ] -} From 4bf98613505631009bb9b799d11d81bc9f3d3fa9 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 3 Nov 2015 15:36:02 -0800 Subject: [PATCH 0667/1003] Forgot license header. --- examples/away.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/away.js b/examples/away.js index c183f236fd..dba26e0697 100644 --- a/examples/away.js +++ b/examples/away.js @@ -1,7 +1,16 @@ "use strict"; /*jslint vars: true, plusplus: true*/ /*global HMD, AudioDevice, MyAvatar, Controller, Script, Overlays, print*/ - +// +// away.js +// examples +// +// Created by Howard Stearns 11/3/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 +// // Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key. // See MAIN CONTROL, below, for what "paused" actually does. From 0c8a7129357fb3c5a2d46ff89ed735142b3d3af0 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 15:36:11 -0800 Subject: [PATCH 0668/1003] removed comment --- .../entities-renderer/src/RenderableParticleEffectEntityItem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index db0972a180..ddeccc141c 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -342,7 +342,6 @@ void RenderableParticleEffectEntityItem::createPipelines() { bool writeToDepthBuffer = !_additiveBlending; - qDebug() << "ADDITIVE BLENDING" << _additiveBlending; state->setDepthTest(true, writeToDepthBuffer, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, destinationColorBlendArg, gpu::State::FACTOR_ALPHA, From 57bc2f3093dc6ebe47233ad91a2af53fdcedb18d Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 3 Nov 2015 15:37:36 -0800 Subject: [PATCH 0669/1003] Removing dead js files --- examples/controllers/controllerMappings.js | 101 --------------------- 1 file changed, 101 deletions(-) delete mode 100644 examples/controllers/controllerMappings.js diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js deleted file mode 100644 index 3848f62096..0000000000 --- a/examples/controllers/controllerMappings.js +++ /dev/null @@ -1,101 +0,0 @@ - -// -// 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]); -}); -*/ - - -Controller.hardwareChanged.connect(function () { - 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 From 60f35ce8c7aec8eb613ad5000544bf968c80e089 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 15:46:58 -0800 Subject: [PATCH 0670/1003] discarding pixel if alpha is below threshold --- libraries/entities-renderer/src/textured_particle.slf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 58a3103323..995f0db824 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -18,5 +18,8 @@ out vec4 outFragColor; void main(void) { vec4 color = texture(colorMap, _texCoord0); + if (color.a < 0.1) { + discard; + } outFragColor = color * _color; } From 31f361d49ca445ce84ca7c0a3ca8875554f42a7a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 3 Nov 2015 15:51:18 -0800 Subject: [PATCH 0671/1003] Moved fps timing to beginning of paintGL This should improve accuracy. However the fps displayed can still sometimes be slightly greater then the vsync, this is either due to timer accuracy problems or QTimer delay within OpenGLDisplayPlugin. --- interface/src/Application.cpp | 40 +++++++++++++++++------------------ interface/src/Application.h | 2 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1f04338f99..d31d9de4a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1041,8 +1041,25 @@ void Application::initializeUi() { } void Application::paintGL() { + _frameCount++; - idle(); + // update fps moving average + uint64_t now = usecTimestampNow(); + static uint64_t lastPaintBegin{ now }; + uint64_t diff = now - lastPaintBegin; + if (diff != 0) { + _framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff); + } + + lastPaintBegin = now; + + // update fps once a second + if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) { + _fps = _framesPerSecond.getAverage(); + _lastFramesPerSecondUpdate = now; + } + + idle(now); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("paintGL"); @@ -1318,24 +1335,6 @@ void Application::paintGL() { batch.resetStages(); }); } - - // update fps moving average - { - uint64_t now = usecTimestampNow(); - static uint64_t lastPaintEnd{ now }; - uint64_t diff = now - lastPaintEnd; - if (diff != 0) { - _framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff); - } - lastPaintEnd = now; - - // update fps once a second - if (now - _lastFramesPerSecondUpdate > USECS_PER_SECOND) { - _fps = _framesPerSecond.getAverage(); - _lastFramesPerSecondUpdate = now; - } - _frameCount++; - } } void Application::runTests() { @@ -2107,7 +2106,7 @@ void Application::ping() { } } -void Application::idle() { +void Application::idle(uint64_t now) { if (_aboutToQuit) { return; // bail early, nothing to do here. } @@ -2130,7 +2129,6 @@ void Application::idle() { { PROFILE_RANGE(__FUNCTION__); - uint64_t now = usecTimestampNow(); static uint64_t lastIdleStart{ now }; uint64_t idleStartToStartDuration = now - lastIdleStart; if (idleStartToStartDuration != 0) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 0fd8c74494..212687c11e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -307,7 +307,7 @@ public slots: private slots: void clearDomainOctreeDetails(); void ping(); - void idle(); + void idle(uint64_t now); void aboutToQuit(); void handleScriptEngineLoaded(const QString& scriptFilename); From d8797ef9d76ca112746c974e6f885d38200ef93f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 3 Nov 2015 15:56:37 -0800 Subject: [PATCH 0672/1003] Initialize relativePosition/Rotation --- examples/controllers/handControllerGrab.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 936a00db07..a8919105d1 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -176,6 +176,9 @@ function MyController(hand) { this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; + + this.offsetPosition = { x: 0.0, y: 0.0, z: 0.0 }; + this.offsetRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; var _this = this; From 470f814776e4e183f73e98bb6c74edb3228eb6ef Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 16:39:16 -0800 Subject: [PATCH 0673/1003] Fixed glitches with large particles being sorted along wrong axis --- .../src/RenderableParticleEffectEntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index ddeccc141c..2d0c40ac56 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -218,10 +218,10 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { // build vertices from particle positions and radiuses glm::vec3 frustumPosition = frustum->getPosition(); + glm::vec3 dir = frustum->getDirection(); for (auto&& particle : particleDetails) { - glm::vec3 particleDirection = particle.position - frustumPosition; - glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), particleDirection)); - glm::vec3 up = glm::normalize(glm::cross(right, particleDirection)); + glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir)); + glm::vec3 up = glm::normalize(glm::cross(right, dir)); glm::vec3 upOffset = up * particle.radius; glm::vec3 rightOffset = right * particle.radius; From 31fe5fa953d9bdaf210d7f1309e921539f3c9917 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 17:15:10 -0800 Subject: [PATCH 0674/1003] Bump version num, use different shader for additive blending effect --- .../RenderableParticleEffectEntityItem.cpp | 10 +++++++- .../src/textured_particle.slf | 3 --- .../src/textured_particle_alpha_discard.slf | 25 +++++++++++++++++++ .../entities/src/ParticleEffectEntityItem.cpp | 4 ++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 6 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 libraries/entities-renderer/src/textured_particle_alpha_discard.slf diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 2d0c40ac56..9307aee410 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -23,6 +23,7 @@ #include "untextured_particle_frag.h" #include "textured_particle_vert.h" #include "textured_particle_frag.h" +#include "textured_particle_alpha_discard_frag.h" class ParticlePayload { public: @@ -347,7 +348,14 @@ void RenderableParticleEffectEntityItem::createPipelines() { destinationColorBlendArg, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert))); - auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); + gpu::ShaderPointer fragShader; + if (_additiveBlending) { + fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag))); + } + else { + //If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts + fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag))); + } auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); diff --git a/libraries/entities-renderer/src/textured_particle.slf b/libraries/entities-renderer/src/textured_particle.slf index 995f0db824..58a3103323 100644 --- a/libraries/entities-renderer/src/textured_particle.slf +++ b/libraries/entities-renderer/src/textured_particle.slf @@ -18,8 +18,5 @@ out vec4 outFragColor; void main(void) { vec4 color = texture(colorMap, _texCoord0); - if (color.a < 0.1) { - discard; - } outFragColor = color * _color; } diff --git a/libraries/entities-renderer/src/textured_particle_alpha_discard.slf b/libraries/entities-renderer/src/textured_particle_alpha_discard.slf new file mode 100644 index 0000000000..389744449a --- /dev/null +++ b/libraries/entities-renderer/src/textured_particle_alpha_discard.slf @@ -0,0 +1,25 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// fragment shader +// +// 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 +// + +uniform sampler2D colorMap; + +in vec4 _color; +in vec2 _texCoord0; + +out vec4 outFragColor; + +void main(void) { + vec4 color = texture(colorMap, _texCoord0); + if (color.a < 0.1) { + discard; + } + outFragColor = color * _color; +} diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 00ff0ed72a..263d7dce0c 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -440,7 +440,6 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch if (args.bitstreamVersion < VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER) { // OLD PROP_EMIT_VELOCITY FAKEOUT SKIP_ENTITY_PROPERTY(PROP_EMIT_SPEED, glm::vec3); - SKIP_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool) } if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLE_MODIFICATIONS) { @@ -487,6 +486,9 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); + } + + if (args.bitstreamVersion >= VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING) { READ_ENTITY_PROPERTY(PROP_ADDITIVE_BLENDING, bool, setAdditiveBlending); } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index a46a9693ac..24034ff9b3 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_BIS; + return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 553c12f8e3..82d905bf28 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -145,5 +145,6 @@ 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; +const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; #endif // hifi_PacketHeaders_h From af0c0e6a1b53329e10cab139887936e3da1c6a76 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 3 Nov 2015 17:32:22 -0800 Subject: [PATCH 0675/1003] spacing fix --- .../src/RenderableParticleEffectEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 9307aee410..6f3b40eb1e 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -354,7 +354,7 @@ void RenderableParticleEffectEntityItem::createPipelines() { } else { //If we are sorting and have no additive blending, we want to discard pixels with low alpha to avoid inter-particle entity artifacts - fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag))); + fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_alpha_discard_frag))); } auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader)); _texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state)); From d7279e4c883aabec5b4549ffd9e72803a6411030 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 02:51:23 +0100 Subject: [PATCH 0676/1003] Support for Camera Entities. - New entity type: camera, with just the basic properties of an entity (position, rotation, name etc.). - New CameraMode: CameraEntity - Example script using the new cameraEntity - Updated edit.js to use the CameraEntities (with button to preview the camera) --- examples/edit.js | 23 ++++++++++++++++ interface/src/Application.cpp | 21 +++++++++++++-- interface/src/Camera.cpp | 27 +++++++++++++++++++ interface/src/Camera.h | 8 ++++++ interface/src/Menu.cpp | 3 +++ interface/src/Menu.h | 1 + libraries/entities/src/CameraEntityItem.cpp | 30 +++++++++++++++++++++ libraries/entities/src/CameraEntityItem.h | 27 +++++++++++++++++++ libraries/entities/src/EntityTypes.cpp | 4 ++- libraries/entities/src/EntityTypes.h | 3 ++- 10 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 libraries/entities/src/CameraEntityItem.cpp create mode 100644 libraries/entities/src/CameraEntityItem.h diff --git a/examples/edit.js b/examples/edit.js index 7a16030afc..eccb9a152c 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -155,6 +155,7 @@ var toolBar = (function() { newWebButton, newZoneButton, newPolyVoxButton, + newCameraButton, browseMarketplaceButton; function initialize() { @@ -299,6 +300,20 @@ var toolBar = (function() { visible: false }); + newCameraButton = toolBar.addTool({ + imageURL: toolIconUrl + "polyvox.svg", + subImage: { + x: 0, + y: 0, + width: 256, + height: 256 + }, + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: false + }); + that.setActive(false); } @@ -345,6 +360,7 @@ var toolBar = (function() { toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newPolyVoxButton, doShow); + toolBar.showTool(newCameraButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -601,6 +617,13 @@ var toolBar = (function() { } + return true; + } + if (newCameraButton === toolBar.clicked(clickedOverlay)) { + createNewEntity({ + type: "Camera" + }); + return true; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d41679bea..f34462f2eb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1187,6 +1187,19 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; + } else if (_myCamera.getMode() == CAMERA_MODE_CAMERA_ENTITY) { + EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); + if (cameraEntity != nullptr) { + if (isHMDMode()) { + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(cameraEntity->getPosition() + hmdOffset); + glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); + _myCamera.setRotation(cameraEntity->getRotation() * hmdRotation); + } else { + _myCamera.setPosition(cameraEntity->getPosition()); + _myCamera.setRotation(cameraEntity->getRotation()); + } + } } // Update camera position if (!isHMDMode()) { @@ -2640,8 +2653,8 @@ void Application::cycleCamera() { menu->setIsOptionChecked(MenuOption::ThirdPerson, false); menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); - } else if (menu->isOptionChecked(MenuOption::IndependentMode)) { - // do nothing if in independe mode + } else if (menu->isOptionChecked(MenuOption::IndependentMode) || menu->isOptionChecked(MenuOption::CameraEntityMode)) { + // do nothing if in independent or camera entity modes return; } cameraMenuChanged(); // handle the menu change @@ -2668,6 +2681,10 @@ void Application::cameraMenuChanged() { if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { _myCamera.setMode(CAMERA_MODE_INDEPENDENT); } + } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) { + if (_myCamera.getMode() != CAMERA_MODE_CAMERA_ENTITY) { + _myCamera.setMode(CAMERA_MODE_CAMERA_ENTITY); + } } } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index 17c745bdba..ae654c4906 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -28,6 +28,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_MIRROR; } else if (mode == "independent") { return CAMERA_MODE_INDEPENDENT; + } else if (mode == "camera entity") { + return CAMERA_MODE_CAMERA_ENTITY; } return CAMERA_MODE_NULL; } @@ -41,6 +43,8 @@ QString modeToString(CameraMode mode) { return "mirror"; } else if (mode == CAMERA_MODE_INDEPENDENT) { return "independent"; + } else if (mode == CAMERA_MODE_CAMERA_ENTITY) { + return "camera entity"; } return "unknown"; } @@ -94,6 +98,26 @@ void Camera::setMode(CameraMode mode) { emit modeUpdated(modeToString(mode)); } +QUuid Camera::getCameraEntity() const { + if (_cameraEntity != nullptr) { + return _cameraEntity->getID(); + } + return QUuid(); +}; + +void Camera::setCameraEntity(QUuid cameraEntityID) { + EntityItemPointer entity = qApp->getEntities()->getTree()->findEntityByID(cameraEntityID); + if (entity == nullptr) { + qDebug() << "entity pointer not found"; + return; + } + if (entity->getType() != EntityTypes::Camera) { + qDebug() << "entity type is not camera"; + return; + } + _cameraEntity = entity; +} + void Camera::setProjection(const glm::mat4& projection) { _projection = projection; } @@ -118,6 +142,9 @@ void Camera::setModeString(const QString& mode) { case CAMERA_MODE_INDEPENDENT: Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); break; + case CAMERA_MODE_CAMERA_ENTITY: + Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); + break; default: break; } diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 7f7515cf5f..7aa31456e9 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -24,6 +24,7 @@ enum CameraMode CAMERA_MODE_FIRST_PERSON, CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, + CAMERA_MODE_CAMERA_ENTITY, NUM_CAMERA_MODES }; @@ -36,6 +37,7 @@ class Camera : public QObject { Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString) + Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) public: Camera(); @@ -49,6 +51,8 @@ public: void loadViewFrustum(ViewFrustum& frustum) const; ViewFrustum toViewFrustum() const; + EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; } + public slots: QString getModeString() const; void setModeString(const QString& mode); @@ -68,6 +72,9 @@ public slots: const glm::mat4& getProjection() const { return _projection; } void setProjection(const glm::mat4& projection); + QUuid getCameraEntity() const; + void setCameraEntity(QUuid cameraEntityID); + PickRay computePickRay(float x, float y); // These only work on independent cameras @@ -97,6 +104,7 @@ private: glm::quat _rotation; bool _isKeepLookingAt{ false }; glm::vec3 _lookingAt; + EntityItemPointer _cameraEntity; }; #endif // hifi_Camera_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 1565db2905..88a33c6f34 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -279,6 +279,9 @@ Menu::Menu() { cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu, MenuOption::IndependentMode, 0, false, qApp, SLOT(cameraMenuChanged()))); + cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu, + MenuOption::CameraEntityMode, 0, + false, qApp, SLOT(cameraMenuChanged()))); cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu, MenuOption::FullscreenMirror, 0, // QML Qt::Key_H, false, qApp, SLOT(cameraMenuChanged()))); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 162fad1b9f..5c84bb11fa 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -157,6 +157,7 @@ namespace MenuOption { const QString Bookmarks = "Bookmarks"; const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; + const QString CameraEntityMode = "Camera Entity"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString Collisions = "Collisions"; diff --git a/libraries/entities/src/CameraEntityItem.cpp b/libraries/entities/src/CameraEntityItem.cpp new file mode 100644 index 0000000000..2c6ca903b3 --- /dev/null +++ b/libraries/entities/src/CameraEntityItem.cpp @@ -0,0 +1,30 @@ +// +// CameraEntityItem.cpp +// libraries/entities/src +// +// Created by Thijs Wenker on 11/4/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 "EntitiesLogging.h" +#include "EntityItemID.h" +#include "EntityItemProperties.h" +#include "CameraEntityItem.h" + + +EntityItemPointer CameraEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer result { new CameraEntityItem(entityID, properties) }; + return result; +} + +// our non-pure virtual subclass for now... +CameraEntityItem::CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : + EntityItem(entityItemID) +{ + _type = EntityTypes::Camera; + + setProperties(properties); +} diff --git a/libraries/entities/src/CameraEntityItem.h b/libraries/entities/src/CameraEntityItem.h new file mode 100644 index 0000000000..3b046ddc90 --- /dev/null +++ b/libraries/entities/src/CameraEntityItem.h @@ -0,0 +1,27 @@ +// +// CameraEntityItem.h +// libraries/entities/src +// +// Created by Thijs Wenker on 11/4/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 +// + +#ifndef hifi_CameraEntityItem_h +#define hifi_CameraEntityItem_h + +#include "EntityItem.h" + +class CameraEntityItem : public EntityItem { +public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); + + ALLOW_INSTANTIATION // This class can be instantiated + +}; + +#endif // hifi_CameraEntityItem_h diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 52c2242629..b7d9077f57 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,6 +29,7 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" +#include "CameraEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -48,7 +49,8 @@ REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) -REGISTER_ENTITY_TYPE(PolyLine); +REGISTER_ENTITY_TYPE(PolyLine) +REGISTER_ENTITY_TYPE(Camera); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 30b6edbc07..82bbffe254 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,7 +48,8 @@ public: Line, PolyVox, PolyLine, - LAST = PolyLine + Camera, + LAST = Camera } EntityType; static const QString& getEntityTypeName(EntityType entityType); From 7adf38f7812ded2f4a03b2f178da5f00e2b68ca7 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Tue, 3 Nov 2015 18:22:55 -0800 Subject: [PATCH 0677/1003] Spacemouse migration --- .../Spacemouse/spacemouseExample.js | 12 +- .../resources/controllers/spacemouse.json | 16 +- interface/src/Menu.cpp | 2 +- .../src/input-plugins/SpacemouseManager.cpp | 186 ++++++------------ .../src/input-plugins/SpacemouseManager.h | 8 +- 5 files changed, 82 insertions(+), 142 deletions(-) diff --git a/examples/controllers/Spacemouse/spacemouseExample.js b/examples/controllers/Spacemouse/spacemouseExample.js index 904f29cd75..46ccd80fa7 100644 --- a/examples/controllers/Spacemouse/spacemouseExample.js +++ b/examples/controllers/Spacemouse/spacemouseExample.js @@ -37,32 +37,32 @@ function update(deltaTime) { if(spacemouseCheck){ if(Controller.getValue(Controller.Standard.LY) != 0){ toggleFirstMove(); - print("Controller TX: " + Controller.getValue(Controller.Standard.LY)); + print("- Controller TY: " + Controller.getValue(Controller.Standard.LY)); } if(Controller.getValue(Controller.Standard.LX) != 0){ toggleFirstMove(); - print("Controller TZ: " + Controller.getValue(Controller.Standard.LX)); + print("- Controller RZ: " + Controller.getValue(Controller.Standard.LX)); } if(Controller.getValue(Controller.Standard.LT) != 0){ toggleFirstMove(); - print("Controller TY: " + Controller.getValue(Controller.Standard.LT)); + print("- Controller LEFTB: " + Controller.getValue(Controller.Standard.LT)); } if(Controller.getValue(Controller.Standard.RY) != 0){ toggleFirstMove(); - print("Controller RX: " + Controller.getValue(Controller.Standard.RY)); + print("- Controller TZ: " + Controller.getValue(Controller.Standard.RY)); } if(Controller.getValue(Controller.Standard.RX) != 0){ toggleFirstMove(); - print("Controller RZ: " + Controller.getValue(Controller.Standard.RX)); + print("- Controller TX: " + Controller.getValue(Controller.Standard.RX)); } if(Controller.getValue(Controller.Standard.RT) != 0){ toggleFirstMove(); - print("Controller RY: " + Controller.getValue(Controller.Standard.RT)); + print("- Controller RIGHTB: " + Controller.getValue(Controller.Standard.RT)); } firstmove = 1; diff --git a/interface/resources/controllers/spacemouse.json b/interface/resources/controllers/spacemouse.json index 61af605512..e02d0d4101 100644 --- a/interface/resources/controllers/spacemouse.json +++ b/interface/resources/controllers/spacemouse.json @@ -1,15 +1,15 @@ { "name": "Spacemouse to Standard", "channels": [ - { "from": "Spacemouse.TranslateX", "to": "Standard.LY" }, - { "from": "Spacemouse.TranslateZ", "filters": "invert", "to": "Standard.LX" }, - - { "from": "Spacemouse.RotateX", "to": "Standard.RY" }, - { "from": "Spacemouse.RotateZ", "to": "Standard.RX" }, - - { "from": "Spacemouse.LeftButton", "to": "Standard.Back" }, - { "from": "Spacemouse.RightButton", "to": "Standard.Start" } + { "from": "Spacemouse.TranslateX", "to": "Standard.RX" }, + { "from": "Spacemouse.TranslateY", "to": "Standard.LY" }, + { "from": "Spacemouse.TranslateZ", "to": "Standard.RY" }, + + { "from": "Spacemouse.RotateZ", "to": "Standard.LX" }, + + { "from": "Spacemouse.LeftButton", "to": "Standard.LT" }, + { "from": "Spacemouse.RightButton", "to": "Standard.RT" } ] } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c15ce4a066..162b713948 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -465,7 +465,7 @@ Menu::Menu() { avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, &SpacemouseManager::getInstance(), SLOT(toggleConnexion(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, &SpacemouseManager::getInstance(), SLOT(toggleSpacemouse(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp index 3078f99a23..3f68be8039 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -34,26 +34,12 @@ void SpacemouseDevice::focusOutEvent() { }; -//SpacemouseDevice& SpacemouseDevice::getInstance() { -// static SpacemouseDevice sharedInstance; -// return sharedInstance; -//} - - - -//std::shared_ptr SpacemouseManager::getDevice(){ -// return instance; -//} - -//SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") {} - - void SpacemouseDevice::handleAxisEvent() { 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; + auto position = cc_position / MAX_AXIS; _axisStateMap[TRANSLATE_X] = position.x; _axisStateMap[TRANSLATE_Y] = position.y; _axisStateMap[TRANSLATE_Z] = position.z; @@ -75,9 +61,9 @@ controller::Input::NamedVector SpacemouseDevice::getAvailableInputs() const { makePair(BUTTON_2, "RightButton"), //makePair(BUTTON_3, "BothButtons"), makePair(TRANSLATE_X, "TranslateX"), - //makePair(TRANSLATE_Y, "TranslateY"), + makePair(TRANSLATE_Y, "TranslateY"), makePair(TRANSLATE_Z, "TranslateZ"), - makePair(ROTATE_X, "RotateX"), + //makePair(ROTATE_X, "RotateX"), //makePair(ROTATE_Y, "RotateY"), makePair(ROTATE_Z, "RotateZ"), @@ -90,45 +76,11 @@ QString SpacemouseDevice::getDefaultMappingConfig() const { return MAPPING_JSON; } -//void SpacemouseDevice::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 SpacemouseDevice::getButton(int channel) const { if (!_buttonPressedMap.empty()) { if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { return 1.0f; - } - else { + } else { return 0.0f; } } @@ -139,8 +91,7 @@ float SpacemouseDevice::getAxis(int channel) const { auto axis = _axisStateMap.find(channel); if (axis != _axisStateMap.end()) { return (*axis).second; - } - else { + } else { return 0.0f; } } @@ -179,18 +130,14 @@ void SpacemouseManager::ManagerFocusOutEvent() { instance->focusOutEvent(); } -#ifdef HAVE_SPACEMOUSE +#ifdef HAVE_3DCONNEXIONCLIENT #ifdef Q_OS_WIN #include - - void SpacemouseManager::toggleSpacemouse(bool shouldEnable) { - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - //if (shouldEnable && spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { - if (shouldEnable && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { + if (shouldEnable) { init(); } if (!shouldEnable && instance->getDeviceID() != controller::Input::INVALID_DEVICE) { @@ -205,13 +152,18 @@ void SpacemouseManager::init() { InitializeRawInput(GetActiveWindow()); QAbstractEventDispatcher::instance()->installNativeEventFilter(this); + + if (instance->getDeviceID() != controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(instance); + UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); + } + } } void SpacemouseManager::destroy() { QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - //int deviceid = spacemousedevice.getDeviceID(); int deviceid = instance->getDeviceID(); auto userInputMapper = DependencyManager::get(); userInputMapper->removeDevice(deviceid); @@ -306,8 +258,7 @@ unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) { if (pid == _3dmouseVirtualKeys[i].pid) { if (hidKeyCode < _3dmouseVirtualKeys[i].nKeys) { virtualkey = _3dmouseVirtualKeys[i].vkeys[hidKeyCode]; - } - else { + } else { virtualkey = V3DK_INVALID; } break; @@ -318,15 +269,12 @@ unsigned short HidToVirtualKey(unsigned long pid, unsigned short hidKeyCode) { } bool SpacemouseManager::RawInputEventFilter(void* msg, long* result) { - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); auto userInputMapper = DependencyManager::get(); - //if (Is3dmouseAttached() && spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { userInputMapper->registerDevice(instance); UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); } - //else if (!Is3dmouseAttached() && spacemousedevice.getDeviceID() != controller::Input::INVALID_DEVICE) { else if (!Is3dmouseAttached() && instance->getDeviceID() != controller::Input::INVALID_DEVICE) { userInputMapper->removeDevice(instance->getDeviceID()); } @@ -361,6 +309,7 @@ const I3dMouseParam& SpacemouseManager::MouseParams() const { //Called with the processed motion data when a 3D mouse event is received void SpacemouseManager::Move3d(HANDLE device, std::vector& motionData) { Q_UNUSED(device); + instance->cc_position = { motionData[0] * 1000, motionData[1] * 1000, motionData[2] * 1000 }; instance->cc_rotation = { motionData[3] * 1500, motionData[4] * 1500, motionData[5] * 1500 }; instance->handleAxisEvent(); @@ -478,8 +427,7 @@ UINT SpacemouseManager::GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT c ::IsWow64Process(GetCurrentProcess(), &bIsWow64); if (!bIsWow64 || pData == NULL) { return ::GetRawInputBuffer(pData, pcbSize, cbSizeHeader); - } - else { + } else { HWND hwndTarget = fWindow; size_t cbDataSize = 0; @@ -493,8 +441,7 @@ UINT SpacemouseManager::GetRawInputBuffer(PRAWINPUT pData, PUINT pcbSize, UINT c if (::GetRawInputData(hRawInput, RID_INPUT, pri, &cbSize, cbSizeHeader) == static_cast(-1)) { if (nCount == 0) { return static_cast(-1); - } - else { + } else { break; } } @@ -534,16 +481,14 @@ void SpacemouseManager::On3dmouseInput() { if (0 == fLast3dmouseInputTime) { dwElapsedTime = 10; // System timer resolution - } - else { + } else { dwElapsedTime = dwNow - fLast3dmouseInputTime; if (fLast3dmouseInputTime > dwNow) { dwElapsedTime = ~dwElapsedTime + 1; } if (dwElapsedTime<1) { dwElapsedTime = 1; - } - else if (dwElapsedTime > 500) { + } else if (dwElapsedTime > 500) { // Check for wild numbers because the device was removed while sending data dwElapsedTime = 10; } @@ -572,8 +517,7 @@ void SpacemouseManager::On3dmouseInput() { // If we have not received data for a while send a zero event if ((--(iterator->second.fTimeToLive)) == 0) { iterator->second.fAxes.assign(6, .0); - } - else if (!iterator->second.fIsDirty) { //!t_bPoll3dmouse && + } else if (!iterator->second.fIsDirty) { //!t_bPoll3dmouse && // If we are not polling then only handle the data that was actually received ++iterator; continue; @@ -591,14 +535,14 @@ void SpacemouseManager::On3dmouseInput() { // Pan Zoom filter // See "Programming for the 3D Mouse", Section 5.1.2 if (!i3dmouseParam.IsPanZoom()) { - // Pan zoom is switched off so set the translation vector values to zero + // Pan zoom is switched off so set the translation vector values to zero motionData[0] = motionData[1] = motionData[2] = 0.; } // Rotate filter // See "Programming for the 3D Mouse", Section 5.1.1 if (!i3dmouseParam.IsRotate()) { - // Rotate is switched off so set the rotation vector values to zero + // Rotate is switched off so set the rotation vector values to zero motionData[3] = motionData[4] = motionData[5] = 0.; } @@ -622,8 +566,7 @@ void SpacemouseManager::On3dmouseInput() { // Now a bit of book keeping before passing on the data if (iterator->second.IsZero()) { iterator = fDevice2Data.erase(iterator); - } - else { + } else { ++iterator; } @@ -643,8 +586,7 @@ void SpacemouseManager::On3dmouseInput() { if (!fDevice2Data.empty()) { fLast3dmouseInputTime = dwNow; - } - else { + } else { fLast3dmouseInputTime = 0; } } @@ -746,22 +688,20 @@ bool SpacemouseManager::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInp //qDebug("Pan/Zoom RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[0], pnRawData[1], pnRawData[2]); - if (pRawInput->data.hid.dwSizeHid >= 13) { // Highspeed package - // Cache the rotation data - deviceData.fAxes[3] = static_cast(pnRawData[3]); - deviceData.fAxes[4] = static_cast(pnRawData[4]); - deviceData.fAxes[5] = static_cast(pnRawData[5]); - deviceData.fIsDirty = true; + //if (pRawInput->data.hid.dwSizeHid >= 13) { // Highspeed package + // // Cache the rotation data + // deviceData.fAxes[3] = static_cast(pnRawData[3]); + // deviceData.fAxes[4] = static_cast(pnRawData[4]); + // deviceData.fAxes[5] = static_cast(pnRawData[5]); + // deviceData.fIsDirty = true; - //qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[3], pnRawData[4], pnRawData[5]); - return true; - } - } - else { // Zero out the data if the app is not in forground + // qDebug("Rotation RI Data =\t0x%x,\t0x%x,\t0x%x\n", pnRawData[3], pnRawData[4], pnRawData[5]); + // return true; + //} + } else { // Zero out the data if the app is not in forground deviceData.fAxes.assign(6, 0.f); } - } - else if (pRawInput->data.hid.bRawData[0] == 0x02) { // Rotation vector + } else if (pRawInput->data.hid.bRawData[0] == 0x02) { // Rotation vector // If we are not in foreground do nothing // The rotation vector was zeroed out with the translation vector in the previous message if (bIsForeground) { @@ -779,8 +719,7 @@ bool SpacemouseManager::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInp return true; } - } - else if (pRawInput->data.hid.bRawData[0] == 0x03) { // Keystate change + } else if (pRawInput->data.hid.bRawData[0] == 0x03) { // Keystate change // this is a package that contains 3d mouse keystate information // bit0=key1, bit=key2 etc. @@ -792,8 +731,7 @@ bool SpacemouseManager::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInp unsigned long dwOldKeystate = fDevice2Keystate[pRawInput->header.hDevice]; if (dwKeystate != 0) { fDevice2Keystate[pRawInput->header.hDevice] = dwKeystate; - } - else { + } else { fDevice2Keystate.erase(pRawInput->header.hDevice); } @@ -807,8 +745,7 @@ bool SpacemouseManager::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInp if (nVirtualKeyCode) { if (dwKeystate & 0x01) { On3dmouseKeyDown(pRawInput->header.hDevice, nVirtualKeyCode); - } - else { + } else { On3dmouseKeyUp(pRawInput->header.hDevice, nVirtualKeyCode); } } @@ -824,13 +761,13 @@ bool SpacemouseManager::TranslateRawInputData(UINT nInputCode, PRAWINPUT pRawInp } MouseParameters::MouseParameters() : -fNavigation(NAVIGATION_OBJECT_MODE), -fPivot(PIVOT_AUTO), -fPivotVisibility(PIVOT_SHOW), -fIsLockHorizon(true), -fIsPanZoom(true), -fIsRotate(true), -fSpeed(SPEED_LOW) + fNavigation(NAVIGATION_OBJECT_MODE), + fPivot(PIVOT_AUTO), + fPivotVisibility(PIVOT_SHOW), + fIsLockHorizon(true), + fIsPanZoom(true), + fIsRotate(true), + fSpeed(SPEED_LOW) { } @@ -921,8 +858,9 @@ void SpacemouseManager::init() { // ...or use this to take over system-wide fConnexionClientID = RegisterConnexionClient(kConnexionClientWildcard, NULL, kConnexionClientModeTakeOver, kConnexionMaskAll); - SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - memcpy(&spacemousedevice.clientId, &fConnexionClientID, (long)sizeof(int)); + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + //memcpy(&spacemousedevice.clientId, &fConnexionClientID, (long)sizeof(int)); + memcpy(&instance->clientId, &fConnexionClientID, (long)sizeof(int)); // A separate API call is required to capture buttons beyond the first 8 SetConnexionClientButtonMask(fConnexionClientID, kConnexionMaskAllButtons); @@ -930,9 +868,9 @@ void SpacemouseManager::init() { // use default switches ConnexionClientControl(fConnexionClientID, kConnexionCtlSetSwitches, kConnexionSwitchesDisabled, NULL); - if (Is3dmouseAttached() && spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { + if (Is3dmouseAttached() && instance->getDeviceID() == controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(&spacemousedevice); + userInputMapper->registerDevice(instance); UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); } //let one axis be dominant @@ -949,32 +887,32 @@ void SpacemouseManager::destroy() { } CleanupConnexionHandlers(); fConnexionClientID = 0; - SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - if (spacemousedevice.getDeviceID() != controller::Input::INVALID_DEVICE) { + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + if (instance->getDeviceID() != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(spacemousedevice.getDeviceID()); - spacemousedevice.setDeviceID(0); + userInputMapper->removeDevice(instance->getDeviceID()); + instance->setDeviceID(controller::Input::INVALID_DEVICE); } } } void DeviceAddedHandler(unsigned int connection) { - SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - if (spacemousedevice.getDeviceID() == controller::Input::INVALID_DEVICE) { + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) { qCWarning(interfaceapp) << "Spacemouse device added "; auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(&spacemousedevice); + userInputMapper->registerDevice(instance); UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); } } void DeviceRemovedHandler(unsigned int connection) { - SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - if (spacemousedevice.getDeviceID() != controller::Input::INVALID_DEVICE) { + //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); + if (instance->getDeviceID() != controller::Input::INVALID_DEVICE) { qCWarning(interfaceapp) << "Spacemouse device removed"; auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(spacemousedevice.getDeviceID()); - spacemousedevice.setDeviceID(controller::Input::INVALID_DEVICE); + userInputMapper->removeDevice(instance->getDeviceID()); + instance->setDeviceID(controller::Input::INVALID_DEVICE); } } diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index 5379af9991..0d6556d059 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -11,6 +11,8 @@ #ifndef hifi_SpacemouseManager_h #define hifi_SpacemouseManager_h +#define HAVE_3DCONNEXIONCLIENT + #include #include #include @@ -19,7 +21,7 @@ #include "InputPlugin.h" -#ifndef HAVE_SPACEMOUSE +#ifndef HAVE_3DCONNEXIONCLIENT class SpacemouseManager : public QObject { Q_OBJECT public: @@ -29,11 +31,11 @@ public: void destroy() {}; bool Is3dmouseAttached() { return false; }; public slots: - void toggleConnexion(bool shouldEnable) {}; + void toggleSpacemouse(bool shouldEnable) {}; }; #endif -#ifdef HAVE_SPACEMOUSE +#ifdef HAVE_3DCONNEXIONCLIENT // the windows connexion rawinput #ifdef Q_OS_WIN From d60661ff8a75bf5d10029778cc53c8de8bf0f464 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 03:24:32 +0100 Subject: [PATCH 0678/1003] preview camera button in edit.js script --- examples/edit.js | 13 +++++++++---- examples/html/entityProperties.html | 11 +++++++++-- examples/libraries/entityPropertyDialogBox.js | 13 ++++++++++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index eccb9a152c..ff4021c46f 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -301,12 +301,12 @@ var toolBar = (function() { }); newCameraButton = toolBar.addTool({ - imageURL: toolIconUrl + "polyvox.svg", + imageURL: toolIconUrl + "light.svg", subImage: { x: 0, - y: 0, - width: 256, - height: 256 + y: Tool.IMAGE_WIDTH, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT }, width: toolWidth, height: toolHeight, @@ -1640,6 +1640,11 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } + } else if (data.action == "previewCamera") { + if (selectionManager.hasSelection()) { + Camera.mode = "camera entity"; + Camera.cameraEntity = selectionManager.selections[0]; + } } else if (data.action == "rescaleDimensions") { var multiplier = data.percentage / 100; if (selectionManager.hasSelection()) { diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 412b413b2b..bc6a6920d1 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -382,7 +382,7 @@ var elHyperlinkHref = document.getElementById("property-hyperlink-href"); var elHyperlinkDescription = document.getElementById("property-hyperlink-description"); - + var elPreviewCameraButton = document.getElementById("preview-camera-button"); if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { @@ -931,6 +931,12 @@ action: "centerAtmosphereToZone", })); }); + elPreviewCameraButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "previewCamera" + })); + }); window.onblur = function() { // Fake a change event @@ -1032,7 +1038,7 @@
- +
@@ -1044,6 +1050,7 @@
+
diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index 4fd24756e0..b47e26579d 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -204,7 +204,7 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Collisions Will Move:", type: "checkbox", value: properties.collisionsWillMove }); index++; array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL }); - index++; + index++; array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) }); index++; @@ -260,6 +260,12 @@ EntityPropertyDialogBox = (function () { array.push({ label: "Cutoff (in degrees):", value: properties.cutoff }); index++; } + + if (properties.type == "Camera") { + array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); + index++; + } + array.push({ button: "Cancel" }); index++; @@ -268,6 +274,11 @@ EntityPropertyDialogBox = (function () { }; Window.inlineButtonClicked.connect(function (name) { + if (name == "previewCamera") { + Camera.mode = "camera entity"; + Camera.cameraEntity = propertiesForEditedEntity.id; + } + if (name == "resetDimensions") { Window.reloadNonBlockingForm([ { value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX }, From df4be6c0ba8a5829f2f05678e0a3a4b9509b7dad Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 3 Nov 2015 10:29:41 -0800 Subject: [PATCH 0679/1003] Add growth script to examples --- examples/growth.js | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 examples/growth.js diff --git a/examples/growth.js b/examples/growth.js new file mode 100644 index 0000000000..8a730da6c1 --- /dev/null +++ b/examples/growth.js @@ -0,0 +1,75 @@ +// +// growth.js +// +// Created by Zachary Pomerantz 11/2/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 +// +var RADIUS = 5; +var LIFETIME = 6000; + +var size = 1; +var center = MyAvatar.position; +var positions = [ + Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.FRONT)), + Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.FRONT)), + Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.RIGHT)), + Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.RIGHT)), + Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.UP)), + Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.UP)), +]; + +var spheres = map(positions, getSphere); + +Script.update.connect(function(delta) { + size += delta; // grow by 1 unit/s + each(spheres, function(sphere) { + Entities.editEntity(sphere, { dimensions: getDimensions(size) }); + }); +}); +Script.scriptEnding.connect(function() { + each(spheres, function(sphere) { + Entities.deleteEntity(sphere); + }); +}); + +// Entity helpers + +function getSphere(position) { + return Entities.addEntity({ + type: 'Sphere', + position: position, + dimensions: getDimensions(size), + color: getColor(), + gravity: Vec3.ZERO, + lifetime: LIFETIME, + ignoreCollisions: true, + }); +} + +function getDimensions(size) { + return { x: size, y: size, z: size }; +} + +function getColor() { + return { red: getValue(), green: getValue(), blue: getValue() }; + function getValue() { return Math.floor(Math.random() * 255); } +} + +// Functional helpers + +function map(list, iterator) { + var result = []; + for (var i = 0; i < list.length; i++) { + result.push(iterator(list[i], i, list)); + } + return result; +} + +function each(list, iterator) { + for (var i = 0; i < list.length; i++) { + iterator(list[i], i, list); + } +} From ade0862948ae8d010d8450b6d4056716348e388d Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Wed, 4 Nov 2015 00:49:40 -0800 Subject: [PATCH 0680/1003] Spacemouse fixes --- .../controllers/Spacemouse/spacemouseExample.js | 8 ++++---- interface/resources/controllers/spacemouse.json | 4 ++-- .../src/input-plugins/SpacemouseManager.cpp | 14 +++----------- .../src/input-plugins/SpacemouseManager.h | 9 ++------- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/examples/controllers/Spacemouse/spacemouseExample.js b/examples/controllers/Spacemouse/spacemouseExample.js index 46ccd80fa7..92e89055b7 100644 --- a/examples/controllers/Spacemouse/spacemouseExample.js +++ b/examples/controllers/Spacemouse/spacemouseExample.js @@ -45,9 +45,9 @@ function update(deltaTime) { print("- Controller RZ: " + Controller.getValue(Controller.Standard.LX)); } - if(Controller.getValue(Controller.Standard.LT) != 0){ + if(Controller.getValue(Controller.Standard.LB) != 0){ toggleFirstMove(); - print("- Controller LEFTB: " + Controller.getValue(Controller.Standard.LT)); + print("- Controller LEFTB: " + Controller.getValue(Controller.Standard.LB)); } if(Controller.getValue(Controller.Standard.RY) != 0){ @@ -60,9 +60,9 @@ function update(deltaTime) { print("- Controller TX: " + Controller.getValue(Controller.Standard.RX)); } - if(Controller.getValue(Controller.Standard.RT) != 0){ + if(Controller.getValue(Controller.Standard.RB) != 0){ toggleFirstMove(); - print("- Controller RIGHTB: " + Controller.getValue(Controller.Standard.RT)); + print("- Controller RIGHTB: " + Controller.getValue(Controller.Standard.RB)); } firstmove = 1; diff --git a/interface/resources/controllers/spacemouse.json b/interface/resources/controllers/spacemouse.json index e02d0d4101..adcfef71a6 100644 --- a/interface/resources/controllers/spacemouse.json +++ b/interface/resources/controllers/spacemouse.json @@ -8,8 +8,8 @@ { "from": "Spacemouse.RotateZ", "to": "Standard.LX" }, - { "from": "Spacemouse.LeftButton", "to": "Standard.LT" }, - { "from": "Spacemouse.RightButton", "to": "Standard.RT" } + { "from": "Spacemouse.LeftButton", "to": "Standard.LB" }, + { "from": "Spacemouse.RightButton", "to": "Standard.RB" } ] } diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp index 3f68be8039..9c1307a18e 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -21,7 +21,7 @@ const float MAX_AXIS = 75.0f; // max forward = 2x speed -static std::shared_ptr instance = NULL; +static std::shared_ptr instance; SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") { @@ -120,9 +120,9 @@ void SpacemouseDevice::update(float deltaTime, bool jointsCaptured) { SpacemouseManager& SpacemouseManager::getInstance() { static SpacemouseManager sharedInstance; - if (instance == NULL){ + if (instance == nullptr) { new SpacemouseDevice(); - } + } return sharedInstance; } @@ -318,14 +318,12 @@ void SpacemouseManager::Move3d(HANDLE device, std::vector& motionData) { //Called when a 3D mouse key is pressed void SpacemouseManager::On3dmouseKeyDown(HANDLE device, int virtualKeyCode) { Q_UNUSED(device); - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); instance->setButton(virtualKeyCode); } //Called when a 3D mouse key is released void SpacemouseManager::On3dmouseKeyUp(HANDLE device, int virtualKeyCode) { Q_UNUSED(device); - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); instance->setButton(0); } @@ -858,8 +856,6 @@ void SpacemouseManager::init() { // ...or use this to take over system-wide fConnexionClientID = RegisterConnexionClient(kConnexionClientWildcard, NULL, kConnexionClientModeTakeOver, kConnexionMaskAll); - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); - //memcpy(&spacemousedevice.clientId, &fConnexionClientID, (long)sizeof(int)); memcpy(&instance->clientId, &fConnexionClientID, (long)sizeof(int)); // A separate API call is required to capture buttons beyond the first 8 @@ -887,7 +883,6 @@ void SpacemouseManager::destroy() { } CleanupConnexionHandlers(); fConnexionClientID = 0; - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); if (instance->getDeviceID() != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); userInputMapper->removeDevice(instance->getDeviceID()); @@ -897,7 +892,6 @@ void SpacemouseManager::destroy() { } void DeviceAddedHandler(unsigned int connection) { - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) { qCWarning(interfaceapp) << "Spacemouse device added "; auto userInputMapper = DependencyManager::get(); @@ -907,7 +901,6 @@ void DeviceAddedHandler(unsigned int connection) { } void DeviceRemovedHandler(unsigned int connection) { - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); if (instance->getDeviceID() != controller::Input::INVALID_DEVICE) { qCWarning(interfaceapp) << "Spacemouse device removed"; auto userInputMapper = DependencyManager::get(); @@ -934,7 +927,6 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes case kConnexionMsgDeviceState: state = (SpacemouseDeviceState*)messageArgument; if (state->client == fConnexionClientID) { - //SpacemouseDevice& spacemousedevice = SpacemouseDevice::getInstance(); instance->cc_position = { state->axis[0], state->axis[1], state->axis[2] }; instance->cc_rotation = { state->axis[3], state->axis[4], state->axis[5] }; diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index 0d6556d059..26e0155825 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -89,8 +89,7 @@ private: }; class SpacemouseManager : public QObject, public QAbstractNativeEventFilter { -//class SpacemouseManager : public InputPlugin, public controller::InputDevice { - + Q_OBJECT public: SpacemouseManager() {}; @@ -118,10 +117,7 @@ public: } public slots: - void toggleSpacemouse(bool shouldEnable); - - //std::shared_ptr getDevice(); - + void toggleSpacemouse(bool shouldEnable); signals: void Move3d(std::vector& motionData); @@ -193,7 +189,6 @@ class SpacemouseDevice : public QObject, public controller::InputDevice { Q_OBJECT public: - //static SpacemouseDevice& getInstance(); SpacemouseDevice(); enum PositionChannel { TRANSLATE_X, From 270b4b97fcdebde188d20275b73917adb272cc6d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 4 Nov 2015 02:26:02 -0800 Subject: [PATCH 0681/1003] remove a left-over kludge that was causing the appearence of frame-lag in held objects --- interface/src/avatar/AvatarActionHold.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index e7d030e17a..70216eca11 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -109,8 +109,6 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { if (_previousSet) { glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); - // back up along velocity a bit in order to smooth out a "vibrating" appearance - _positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f; } } From 184f793cdd2a17f63257170958802ab5d7d9e7b0 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 19:15:29 +0100 Subject: [PATCH 0682/1003] Camera properties were not stored --- libraries/entities/src/CameraEntityItem.cpp | 54 +++++++++++++++++++ libraries/entities/src/CameraEntityItem.h | 20 +++++++ libraries/entities/src/EntityItemProperties.h | 1 + 3 files changed, 75 insertions(+) diff --git a/libraries/entities/src/CameraEntityItem.cpp b/libraries/entities/src/CameraEntityItem.cpp index 2c6ca903b3..8370447389 100644 --- a/libraries/entities/src/CameraEntityItem.cpp +++ b/libraries/entities/src/CameraEntityItem.cpp @@ -28,3 +28,57 @@ CameraEntityItem::CameraEntityItem(const EntityItemID& entityItemID, const Entit setProperties(properties); } + + +EntityItemProperties CameraEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { + EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class + return properties; +} + +bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "CameraEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties._lastEdited); + } + + return somethingChanged; +} + +int CameraEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + return bytesRead; +} + + +// TODO: eventually only include properties changed since the params.lastViewFrustumSent time +EntityPropertyFlags CameraEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + return requestedProperties; +} + +void CameraEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; +} + diff --git a/libraries/entities/src/CameraEntityItem.h b/libraries/entities/src/CameraEntityItem.h index 3b046ddc90..6256c200c2 100644 --- a/libraries/entities/src/CameraEntityItem.h +++ b/libraries/entities/src/CameraEntityItem.h @@ -22,6 +22,26 @@ public: ALLOW_INSTANTIATION // This class can be instantiated + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; + virtual bool setProperties(const EntityItemProperties& properties); + + // TODO: eventually only include properties changed since the params.lastViewFrustumSent time + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged); + }; #endif // hifi_CameraEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index b95f4d35f4..4522e4fc45 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,6 +64,7 @@ class EntityItemProperties { friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods + friend class CameraEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; From 9de5721a4882d021fc035db69c05e5cc170d0a09 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Wed, 4 Nov 2015 10:23:55 -0800 Subject: [PATCH 0683/1003] set _hmdFollowVelocity to zero when done following --- interface/src/avatar/MyAvatar.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 741a82fdb8..79d608640a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1299,11 +1299,11 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); if (qApp->isHMDMode()) { updateHMDFollowVelocity(); - _characterController.setHMDVelocity(_hmdFollowVelocity); - } else { - _characterController.setHMDVelocity(Vectors::ZERO); + } else if (_isFollowingHMD) { _isFollowingHMD = false; + _hmdFollowVelocity = Vectors::ZERO; } + _characterController.setHMDVelocity(_hmdFollowVelocity); } void MyAvatar::harvestResultsFromPhysicsSimulation() { @@ -1339,6 +1339,7 @@ void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) { // the "adjustment" is more or less complete so stop following _isFollowingHMD = false; _hmdFollowSpeed = 0.0f; + _hmdFollowVelocity = Vectors::ZERO; // and slam the body's transform anyway to eliminate any slight errors glm::vec3 finalBodyPosition = extractTranslation(worldBodyMatrix); nextAttitude(finalBodyPosition, finalBodyRotation); From 1e498a41f09bab4ed3c746b750ccc09b9d9c9a65 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 4 Nov 2015 10:46:20 -0800 Subject: [PATCH 0684/1003] rework search logic --- examples/controllers/handControllerGrab.js | 138 +++++++++++++-------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index a8919105d1..48cc299326 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -74,6 +74,13 @@ var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 5; var MSECS_PER_SEC = 1000.0; +var GRABBABLE_PROPERTIES = ["position", + "rotation", + "gravity", + "ignoreForCollisions", + "collisionsWillMove", + "locked", + "name"]; var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js @@ -234,7 +241,7 @@ function MyController(hand) { this.debugLine = function(closePoint, farPoint, color){ Entities.addEntity({ type: "Line", - name: "Debug Line", + name: "Grab Debug Entity", dimensions: LINE_ENTITY_DIMENSIONS, visible: true, position: closePoint, @@ -249,7 +256,7 @@ function MyController(hand) { if (this.pointer === null) { this.pointer = Entities.addEntity({ type: "Line", - name: "pointer", + name: "grab pointer", dimensions: LINE_ENTITY_DIMENSIONS, visible: true, position: closePoint, @@ -308,6 +315,8 @@ function MyController(hand) { } this.search = function() { + this.grabbedEntity = null; + //if this hand is the one that's disabled, we don't want to search for anything at all if (this.hand === disabledHand) { return; @@ -358,7 +367,6 @@ function MyController(hand) { if (intersection.intersects) { // the ray is intersecting something we can move. var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); - this.grabbedEntity = intersection.entityID; //this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); @@ -372,8 +380,11 @@ function MyController(hand) { disabledHand = 'none'; } + if (intersection.properties.name == "Grab Debug Entity") { + continue; + } + if (typeof grabbableData.grabbable !== 'undefined' && !grabbableData.grabbable) { - this.grabbedEntity = null; continue; } if (intersectionDistance > pickRay.length) { @@ -383,56 +394,89 @@ function MyController(hand) { if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { // the hand is very close to the intersected object. go into close-grabbing mode. if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + return; } else if (!intersection.properties.locked) { + this.grabbedEntity = intersection.entityID; this.setState(STATE_NEAR_GRABBING); + return; } - } else { + } else if (! entityIsGrabbedByOther(intersection.entityID)) { // don't allow two people to distance grab the same object - if (entityIsGrabbedByOther(intersection.entityID)) { - this.grabbedEntity = null; - } else { + if (intersection.properties.collisionsWillMove + && !intersection.properties.locked) { // the hand is far from the intersected object. go into distance-holding mode - if (intersection.properties.collisionsWillMove - && !intersection.properties.locked) { - this.setState(STATE_DISTANCE_HOLDING); - } else { - this.setState(STATE_FAR_GRABBING_NON_COLLIDING); - } + this.grabbedEntity = intersection.entityID; + this.setState(STATE_DISTANCE_HOLDING); + return; + } else if (grabbableData.wantsTrigger) { + this.grabbedEntity = intersection.entityID; + this.setState(STATE_FAR_GRABBING_NON_COLLIDING); + return; } } } } + // forward ray test failed, try sphere test. + if (WANT_DEBUG) { + Entities.addEntity({ + type: "Sphere", + name: "Grab Debug Entity", + dimensions: {x: GRAB_RADIUS, y: GRAB_RADIUS, z: GRAB_RADIUS}, + visible: true, + position: handPosition, + color: { red: 0, green: 255, blue: 0}, + lifetime: 0.1 + }); + } + + var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); + var minDistance = PICK_MAX_DISTANCE; + var i, props, distance, grabbableData; + for (i = 0; i < nearbyEntities.length; i++) { + var grabbableDataForCandidate = + getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); + if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) { + continue; + } + var propsForCandidate = + Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES); + + if (propsForCandidate.type == 'Unknown') { + continue; + } + + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { + continue; + } + + if (propsForCandidate.name == "Grab Debug Entity") { + continue; + } + + if (propsForCandidate.name == "grab pointer") { + continue; + } + + distance = Vec3.distance(propsForCandidate.position, handPosition); + if (distance < minDistance) { + this.grabbedEntity = nearbyEntities[i]; + minDistance = distance; + props = propsForCandidate; + grabbableData = grabbableDataForCandidate; + } + } if (this.grabbedEntity === null) { - // forward ray test failed, try sphere test. - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = PICK_MAX_DISTANCE; - var i, props, distance, grabbableData; - for (i = 0; i < nearbyEntities.length; i++) { - var grabbableDataForCandidate = - getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (!grabbableDataForCandidate.grabbable) { - continue; - } - var propsForCandidate = - Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]); - distance = Vec3.distance(propsForCandidate.position, handPosition); - if (distance < minDistance && propsForCandidate.name !== "pointer") { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - props = propsForCandidate; - grabbableData = grabbableDataForCandidate; - } - } - if (this.grabbedEntity === null) { - return; - } - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } else if (!props.locked) { - this.setState(STATE_NEAR_GRABBING); - } + return; + } + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + return; + } else if (!props.locked) { + this.setState(STATE_NEAR_GRABBING); + return; } }; @@ -440,9 +484,7 @@ function MyController(hand) { 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", - "collisionsWillMove"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var now = Date.now(); // add the action and initialize some variables @@ -492,7 +534,7 @@ function MyController(hand) { 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"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); @@ -585,9 +627,7 @@ function MyController(hand) { this.lineOff(); - var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, - ["position", "rotation", "gravity", - "ignoreForCollisions", "collisionsWillMove"]); + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.activateEntity(this.grabbedEntity, grabbedProperties); if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) { Entities.editEntity(this.grabbedEntity, { From f260c6b12bf6372a41ffab2fa3cb163ba128907c Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Wed, 4 Nov 2015 10:47:39 -0800 Subject: [PATCH 0685/1003] fix bad time measurment in AtRestDetector --- libraries/shared/src/AtRestDetector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index d790e2b066..4c08b30dcd 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -32,7 +32,7 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { uint64_t now = usecTimestampNow(); - float dt = (float)(_lastUpdateTime - now) / (float)USECS_PER_SECOND; + float dt = (float)(now - _lastUpdateTime) / (float)USECS_PER_SECOND; _lastUpdateTime = now; const float TAU = 1.0f; float delta = glm::min(dt / TAU, 1.0f); From fe12891f61dfa2ecefe1a3e1281a7bfeb1be7f36 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 4 Nov 2015 10:48:02 -0800 Subject: [PATCH 0686/1003] whitespace only --- examples/away.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/away.js b/examples/away.js index dba26e0697..eb841a07f8 100644 --- a/examples/away.js +++ b/examples/away.js @@ -31,8 +31,8 @@ function playAwayAnimation() { return {isAway: true, isNotAway: false, isNotMoving: false, ikOverlayAlpha: 0.0}; } if (stopper) { - Script.clearTimeout(stopper); - MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment + Script.clearTimeout(stopper); + MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment } awayAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateAway, null); } @@ -47,16 +47,16 @@ function stopAwayAnimation() { // So... Just give us a fixed amount of time to be done with animation, before we turn ik back on. var backToNormal = false; stopper = Script.setTimeout(function () { - backToNormal = true; - stopper = false; + backToNormal = true; + stopper = false; }, IK_WINDOW_AFTER_GOING_ACTIVE); function animateActive(state) { - if (state.ikOverlayAlpha) { - // Once the right state gets reflected back to us, we don't need the hander any more. - // But we are locked against handler changes during the execution of a handler, so remove asynchronously. - Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0); - } - // It might be cool to "come back to life" by fading the ik overlay back in over a short time. But let's see how this goes. + if (state.ikOverlayAlpha) { + // Once the right state gets reflected back to us, we don't need the hander any more. + // But we are locked against handler changes during the execution of a handler, so remove asynchronously. + Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0); + } + // It might be cool to "come back to life" by fading the ik overlay back in over a short time. But let's see how this goes. return {isAway: false, isNotAway: true, ikOverlayAlpha: backToNormal ? 1.0 : 0.0}; // IWBNI we had a way of deleting an anim var. } activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['isAway', 'isNotAway', 'isNotMoving', 'ikOverlayAlpha']); From 883f9c09bf9c4e917dd4f35fe0be339c6ca2eb73 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 4 Nov 2015 10:49:12 -0800 Subject: [PATCH 0687/1003] add gratuitous whitespace --- examples/away.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/away.js b/examples/away.js index eb841a07f8..5cefd0d609 100644 --- a/examples/away.js +++ b/examples/away.js @@ -38,7 +38,10 @@ function playAwayAnimation() { } function stopAwayAnimation() { MyAvatar.removeAnimationStateHandler(awayAnimationHandlerId); - if (stopper) { print('WARNING: unexpected double stop'); return; } + if (stopper) { + print('WARNING: unexpected double stop'); + return; + } // How do we know when to turn ikOverlayAlpha back on? // It cannot be as soon as we want to stop the away animation, because then things will look goofy as we come out of that animation. // (Imagine an away animation that sits or kneels, and then stands back up when coming out of it. If head is at the HMD, then it won't From ce5e89ce66efbade8c20ebd820fb8d700fcb4546 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 4 Nov 2015 10:53:53 -0800 Subject: [PATCH 0688/1003] removing the pulsing mechanism in MyAvatar and making a new stadnard.json mapping file --- interface/resources/controllers/standard.json | 10 ++++----- interface/src/avatar/MyAvatar.cpp | 21 +++---------------- interface/src/avatar/MyAvatar.h | 2 -- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index d312913a23..47ae3633e4 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -2,8 +2,9 @@ "name": "Standard to Action", "channels": [ { "from": "Standard.LY", "to": "Actions.TranslateZ" }, + { "from": "Standard.LX", "to": "Actions.TranslateX" }, - { "from": "Standard.LX", + { "from": "Standard.RX", "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw", "filters": @@ -12,11 +13,8 @@ { "type": "scale", "scale": 15 } ] }, - - { "from": "Standard.LX", "to": "Actions.Yaw" }, - - { "from": "Standard.RX", "to": "Actions.TranslateX" }, - { "from": "Standard.RY", "filters": "invert", "to": "Actions.TranslateY" }, + { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RY", "when": "!Application.InHMD", "to": "Actions.Pitch" }, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 741a82fdb8..b1767acef6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -53,7 +53,6 @@ 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 @@ -259,23 +258,9 @@ void MyAvatar::simulate(float deltaTime) { 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); - - if (_pulseUpdate) { - _lastStepPulse = now; - _pulseUpdate = false; - } } { @@ -1623,7 +1608,7 @@ void MyAvatar::updateOrientation(float deltaTime) { // 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) { + if (_driveKeys[STEP_YAW] != 0.0f) { totalBodyYaw += _driveKeys[STEP_YAW]; } @@ -1692,9 +1677,9 @@ 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) { - } + float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]); if (keyboardInput) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ec33d22d8d..d6f51636f3 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -420,8 +420,6 @@ private: AtRestDetector _hmdAtRestDetector; bool _lastIsMoving { false }; - quint64 _lastStepPulse { 0 }; - bool _pulseUpdate { false }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); From 3fdd7ca470d9aae563c5752ea65796063684a44f Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 4 Nov 2015 10:57:03 -0800 Subject: [PATCH 0689/1003] obscure the code and increase the likelihood of future whitespace-only diffs --- examples/away.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/away.js b/examples/away.js index 5cefd0d609..fa7925a545 100644 --- a/examples/away.js +++ b/examples/away.js @@ -98,16 +98,16 @@ function goActive(event) { if (event.text === '.') { goAway(event); } - return; + } else { + isAway = false; + print('going "active"'); + if (!wasMuted) { + AudioDevice.toggleMute(); + } + MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. + stopAwayAnimation(); + hideOverlay(); } - isAway = false; - print('going "active"'); - if (!wasMuted) { - AudioDevice.toggleMute(); - } - MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. - stopAwayAnimation(); - hideOverlay(); } Controller.keyPressEvent.connect(goActive); Script.scriptEnding.connect(goActive); From d4259931b23f3afedb49a0f9313095754fda78dc Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 4 Nov 2015 10:57:39 -0800 Subject: [PATCH 0690/1003] Setting the step yaw to 22.5 degrees --- 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 47ae3633e4..5de188c0b6 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -10,7 +10,7 @@ "filters": [ { "type": "pulse", "interval": 0.5 }, - { "type": "scale", "scale": 15 } + { "type": "scale", "scale": 22.5 } ] }, { "from": "Standard.RX", "to": "Actions.Yaw" }, From c2f9a495980edfed6fdfecea2796186f473d876c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 4 Nov 2015 11:15:57 -0800 Subject: [PATCH 0691/1003] don't grab light --- examples/controllers/handControllerGrab.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 48cc299326..2d82896e26 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -448,6 +448,10 @@ function MyController(hand) { continue; } + if (propsForCandidate.type == 'Light') { + continue; + } + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { continue; } From 128a0c2397eeae266ce068023d1271f6c562e96f Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 20:23:03 +0100 Subject: [PATCH 0692/1003] securityCamera.js - Camera Entity example --- examples/example/securityCamera.js | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 examples/example/securityCamera.js diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js new file mode 100644 index 0000000000..224eb9fac9 --- /dev/null +++ b/examples/example/securityCamera.js @@ -0,0 +1,55 @@ +// +// securityCamera.js +// examples/example +// +// Created by Thijs Wenker on November 4, 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 +// + +const CAMERA_OFFSET = {x: 0, y: 4, z: -14}; +const LOOKAT_START_OFFSET = {x: -10, y: 0, z: 14}; +const LOOKAT_END_OFFSET = {x: 10, y: 0, z: 14}; + +var lookatTargets = [Vec3.sum(MyAvatar.position, LOOKAT_START_OFFSET), Vec3.sum(MyAvatar.position, LOOKAT_END_OFFSET)]; +var currentTarget = 0; + +var forward = true; + +var oldCameraMode = Camera.mode; + +var cameraLookAt = function(cameraPos, lookAtPos) { + var lookAtRaw = Quat.lookAt(cameraPos, lookAtPos, Vec3.UP); + lookAtRaw.w = -lookAtRaw.w; + return lookAtRaw; +}; + +cameraEntity = Entities.addEntity({ + type: "Camera", + position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) +}); + +Camera.mode = "camera entity"; +Camera.cameraEntity = cameraEntity; + +Script.update.connect(function(deltaTime) { + var cameraProperties = Entities.getEntityProperties(cameraEntity, ["position", "rotation"]); + var targetOrientation = cameraLookAt(cameraProperties.position, lookatTargets[currentTarget]); + if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < 0.01 && + Math.abs(targetOrientation.y - cameraProperties.rotation.y) < 0.01 && + Math.abs(targetOrientation.z - cameraProperties.rotation.z) < 0.01 && + Math.abs(targetOrientation.w - cameraProperties.rotation.w) < 0.01 + ) { + currentTarget = (currentTarget + 1) % lookatTargets.length; + return; + } + Entities.editEntity(cameraEntity, {rotation: Quat.mix(cameraProperties.rotation, targetOrientation, deltaTime / 3)}); +}); + +Script.scriptEnding.connect(function() { + Entities.deleteEntity(cameraEntity); + Camera.mode = oldCameraMode; + Camera.cameraEntity = null; +}); From 3c227db08ddd32b3e372e14c47ff45a09a0be43a Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 4 Nov 2015 11:27:16 -0800 Subject: [PATCH 0693/1003] explode heli --- examples/helicopter/explodeHelicopter.js | 62 ++++++++++++++++++++++++ examples/helicopter/helicopter.js | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 examples/helicopter/explodeHelicopter.js diff --git a/examples/helicopter/explodeHelicopter.js b/examples/helicopter/explodeHelicopter.js new file mode 100644 index 0000000000..98e806dbfd --- /dev/null +++ b/examples/helicopter/explodeHelicopter.js @@ -0,0 +1,62 @@ +var partsURLS = [ + "https://s3.amazonaws.com/hifi-public/eric/models/blade.fbx", + "https://s3.amazonaws.com/hifi-public/eric/models/body.fbx", + "https://s3.amazonaws.com/hifi-public/eric/models/tail.fbx", +] + +var parts = []; + +var explodePosition; +var helicopter; +var entities = Entities.findEntities(MyAvatar.position, 2000); +for (i = 0; i < entities.length; i++) { + var name = Entities.getEntityProperties(entities[i], 'name').name; + if (name === "Helicopter") { + helicopter = entities[i]; + explodeHelicopter(Entities.getEntityProperties(helicopter, 'position').position); + } +} + + +function explodeHelicopter(explodePosition) { + Entities.deleteEntity(helicopter); + for (var i = 0; i < partsURLS.length; i++) { + var part = Entities.addEntity({ + type: "Model", + modelURL: partsURLS[i], + position: explodePosition, + shapeType: "box", + damping: 0 + }); + parts.push(part); + } + + Script.setTimeout(function() { + parts.forEach(function(part) { + var naturalDimensions = Entities.getEntityProperties(part, "naturalDimensions").naturalDimensions; + Entities.editEntity(part, { + dimensions: naturalDimensions, + gravity: { + x: 0, + y: -9.6, + z: 0 + }, + velocity: { + x: Math.random(), + y: -10, + z: Math.random() + }, + collisionsWillMove: true + }); + }); + }, 1000); + +} + +function cleanup() { + parts.forEach(function(part) { + Entities.deleteEntity(part); + }); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index 44482e1c85..c1ad0f9d30 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -6,7 +6,7 @@ var spawnPosition = { z: 1041 }; -var speed = .15; +var speed = 0; var helicopterSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/ryan/helicopter.L.wav"); var audioInjector = Audio.playSound(helicopterSound, { From 18d2b184dcf32d2d52dcbe91f7d0c8548edb80f9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 11:39:34 -0800 Subject: [PATCH 0694/1003] Fix ability to enable mappings parsed from JSON in JS --- libraries/controllers/src/controllers/UserInputMapper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index ec32024b91..fae7a2aaa8 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -1029,6 +1029,7 @@ Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { } mapping->routes.push_back(route); } + _mappingsByName[mapping->name] = mapping; return mapping; } From 4fd2027fb4400af3eacaeeaa57987d74307ee0a5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 4 Nov 2015 11:42:21 -0800 Subject: [PATCH 0695/1003] change name --- examples/away.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/away.js b/examples/away.js index fa7925a545..516365218c 100644 --- a/examples/away.js +++ b/examples/away.js @@ -93,7 +93,7 @@ function goAway(event) { playAwayAnimation(); // animation is still seen by others showOverlay(); } -function goActive(event) { +function switchActiveState(event) { if (!isAway || event.isAutoRepeat) { if (event.text === '.') { goAway(event); @@ -109,8 +109,8 @@ function goActive(event) { hideOverlay(); } } -Controller.keyPressEvent.connect(goActive); -Script.scriptEnding.connect(goActive); +Controller.keyPressEvent.connect(switchActiveState); +Script.scriptEnding.connect(switchActiveState); if (HMD.active) { goAway({}); // give a dummy event object } From 017cb2cb6c88ad483a8bfb612b89e3607ffeed03 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 11:53:42 -0800 Subject: [PATCH 0696/1003] Getting rid of HTTPS external dependencies --- cmake/externals/boostconfig/CMakeLists.txt | 3 ++- cmake/externals/bullet/CMakeLists.txt | 6 ++++-- cmake/externals/openvr/CMakeLists.txt | 3 ++- cmake/externals/sdl2/CMakeLists.txt | 4 ++-- cmake/externals/sixense/CMakeLists.txt | 8 ++++---- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cmake/externals/boostconfig/CMakeLists.txt b/cmake/externals/boostconfig/CMakeLists.txt index ed3413a894..0adb349589 100644 --- a/cmake/externals/boostconfig/CMakeLists.txt +++ b/cmake/externals/boostconfig/CMakeLists.txt @@ -4,7 +4,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://github.com/boostorg/config/archive/boost-1.58.0.zip + #URL https://github.com/boostorg/config/archive/boost-1.58.0.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/config-boost-1.58.0.zip URL_MD5 42fa673bae2b7645a22736445e80eb8d CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/bullet/CMakeLists.txt b/cmake/externals/bullet/CMakeLists.txt index 56e6bf0ccc..efe5b694fa 100644 --- a/cmake/externals/bullet/CMakeLists.txt +++ b/cmake/externals/bullet/CMakeLists.txt @@ -17,7 +17,8 @@ include(ExternalProject) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip + # URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.zip URL_MD5 f5e8914fc9064ad32e0d62d19d33d977 CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 -DUSE_DX11=0 LOG_DOWNLOAD 1 @@ -28,7 +29,8 @@ if (WIN32) else () ExternalProject_Add( ${EXTERNAL_NAME} - URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz + #URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz + URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.tgz URL_MD5 70b3c8d202dee91a0854b4cbc88173e8 CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 LOG_DOWNLOAD 1 diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index 2bb84ca637..dea59f41a0 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -7,7 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip + #URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/openvr-0.9.1.zip URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc CONFIGURE_COMMAND "" BUILD_COMMAND "" diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index abd436d571..decd1c6906 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -7,7 +7,7 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-devel-2.0.3-VC.zip URL_MD5 30a333bcbe94bc5016e8799c73e86233 CONFIGURE_COMMAND "" BUILD_COMMAND "" @@ -18,7 +18,7 @@ elseif (APPLE) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip + URL http://hifi-public.s3.amazonaws.com/dependencies/SDL2-2.0.3.zip CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DVIDEO_OPENGL=OFF BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build LOG_DOWNLOAD 1 diff --git a/cmake/externals/sixense/CMakeLists.txt b/cmake/externals/sixense/CMakeLists.txt index 0cbca087a5..c41694344e 100644 --- a/cmake/externals/sixense/CMakeLists.txt +++ b/cmake/externals/sixense/CMakeLists.txt @@ -4,15 +4,15 @@ set(EXTERNAL_NAME sixense) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -#set(SIXENSE_URL "https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip") +#set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_062612.zip") #set(SIXENSE_URL_MD5 "10cc8dc470d2ac1244a88cf04bc549cc") #set(SIXENSE_NEW_LAYOUT 0) -#set(SIXENSE_URL "https://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip") +#set(SIXENSE_URL "http://public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip") #set(SIXENSE_URL_MD5 "752a3901f334124e9cffc2ba4136ef7d") #set(SIXENSE_NEW_LAYOUT 1) -set(SIXENSE_URL "https://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip") +set(SIXENSE_URL "http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_102215.zip") set(SIXENSE_URL_MD5 "93c3a6795cce777a0f472b09532935f1") set(SIXENSE_NEW_LAYOUT 1) @@ -43,7 +43,7 @@ if (WIN32) endif() if (${SIXENSE_NEW_LAYOUT}) - # for 2015 SDKs (using the 2013 versions may be causing the crash) + # for 2015 SDKs (using the VS2013 versions may be causing the crash, so use the VS2010 versions) set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/${ARCH_DIR}/VS2010/release_dll") set(${EXTERNAL_NAME_UPPER}_LIB_PATH "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2010/release_dll") else() From 69f1cfbcb9125aa9d449a5d01b3734bcd51309e8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 4 Nov 2015 12:04:34 -0800 Subject: [PATCH 0697/1003] Fixes for Mac build and crash on startup For some reason the qt code gen is getting confused by the #if 0 in 3DConnextionClient.h, so I added a stub implementation. In Application.cpp the change of moving idle into paintGL had a sideeffect of calling OffscreenGlCanvas before it was initialized. To work around this I added a guard to prevent calling idle before all the gl windows/widgets have been initialized. --- interface/src/Application.cpp | 12 ++++++------ interface/src/Application.h | 3 ++- interface/src/devices/3DConnexionClient.h | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d31d9de4a0..556664ec10 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -940,14 +940,12 @@ void Application::initializeGL() { qCDebug(interfaceapp) << "Created Display Window."; // initialize glut for shape drawing; Qt apparently initializes it on OS X - #ifndef __APPLE__ - static bool isInitialized = false; - if (isInitialized) { + if (_isGLInitialized) { return; } else { - isInitialized = true; + _isGLInitialized = true; } - #endif + // Where the gpuContext is initialized and where the TRUE Backend is created and assigned gpu::Context::init(); _gpuContext = std::make_shared(); @@ -1059,7 +1057,9 @@ void Application::paintGL() { _lastFramesPerSecondUpdate = now; } - idle(now); + if (_isGLInitialized) { + idle(now); + } PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("paintGL"); diff --git a/interface/src/Application.h b/interface/src/Application.h index 212687c11e..5a5d5a015c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -414,7 +414,7 @@ private: bool _dependencyManagerIsSetup; - OffscreenGlCanvas* _offscreenContext; + OffscreenGlCanvas* _offscreenContext {nullptr}; DisplayPluginPointer _displayPlugin; InputPluginList _activeInputPlugins; @@ -548,6 +548,7 @@ private: quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; + bool _isGLInitialized {false}; }; #endif // hifi_Application_h diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index 03a43d4c64..b6fa6a37c3 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -220,4 +220,19 @@ public: #endif +#include +#include + +// stub +class ConnexionClient : public QObject { + Q_OBJECT +public: + static ConnexionClient& getInstance(); + void init() {}; + void destroy() {}; + bool Is3dmouseAttached() { return false; }; +public slots: + void toggleConnexion(bool shouldEnable) {}; +}; + #endif // defined(hifi_3DConnexionClient_h) From 0ebd6e47cc33668092c67bc32c3b9150e59adb78 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 21:21:03 +0100 Subject: [PATCH 0698/1003] fix magic number --- examples/example/securityCamera.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js index 224eb9fac9..19c381d3df 100644 --- a/examples/example/securityCamera.js +++ b/examples/example/securityCamera.js @@ -12,6 +12,7 @@ const CAMERA_OFFSET = {x: 0, y: 4, z: -14}; const LOOKAT_START_OFFSET = {x: -10, y: 0, z: 14}; const LOOKAT_END_OFFSET = {x: 10, y: 0, z: 14}; +const TINY_VALUE = 0.001; var lookatTargets = [Vec3.sum(MyAvatar.position, LOOKAT_START_OFFSET), Vec3.sum(MyAvatar.position, LOOKAT_END_OFFSET)]; var currentTarget = 0; @@ -37,11 +38,11 @@ Camera.cameraEntity = cameraEntity; Script.update.connect(function(deltaTime) { var cameraProperties = Entities.getEntityProperties(cameraEntity, ["position", "rotation"]); var targetOrientation = cameraLookAt(cameraProperties.position, lookatTargets[currentTarget]); - if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < 0.01 && - Math.abs(targetOrientation.y - cameraProperties.rotation.y) < 0.01 && - Math.abs(targetOrientation.z - cameraProperties.rotation.z) < 0.01 && - Math.abs(targetOrientation.w - cameraProperties.rotation.w) < 0.01 - ) { + if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < TINY_VALUE && + Math.abs(targetOrientation.y - cameraProperties.rotation.y) < TINY_VALUE && + Math.abs(targetOrientation.z - cameraProperties.rotation.z) < TINY_VALUE && + Math.abs(targetOrientation.w - cameraProperties.rotation.w) < TINY_VALUE) { + currentTarget = (currentTarget + 1) % lookatTargets.length; return; } From 42cbccc725191bdf922e8ccb8ca7d6b3fad621ca Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 4 Nov 2015 21:39:22 +0100 Subject: [PATCH 0699/1003] fix stretchy HMD view on entity camera --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eb913ce2f6..35aeba8602 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1204,13 +1204,13 @@ void Application::paintGL() { EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); if (cameraEntity != nullptr) { if (isHMDMode()) { - glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); - _myCamera.setPosition(cameraEntity->getPosition() + hmdOffset); glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix()); _myCamera.setRotation(cameraEntity->getRotation() * hmdRotation); + glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix()); + _myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset)); } else { - _myCamera.setPosition(cameraEntity->getPosition()); _myCamera.setRotation(cameraEntity->getRotation()); + _myCamera.setPosition(cameraEntity->getPosition()); } } } From 6cfc0b9487bf820f7b2210243b9d0ca3ebd89d5c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Wed, 4 Nov 2015 13:07:38 -0800 Subject: [PATCH 0700/1003] update openssl binaries link --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 72dfa5725e..5abb3ae4e7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -2,7 +2,7 @@ * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2 * [Qt](http://www.qt.io/download-open-source) ~> 5.4.1 -* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m +* [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) From 4dec31d524a2d835f05e54111785bca47e08de91 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 13:20:47 -0800 Subject: [PATCH 0701/1003] Fix crash in Oculus simulator --- plugins/oculus/src/OculusDebugDisplayPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp index 2021ce1c5a..7a8b355ddd 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp @@ -25,7 +25,7 @@ bool OculusDebugDisplayPlugin::isSupported() const { } void OculusDebugDisplayPlugin::customizeContext() { - WindowOpenGLDisplayPlugin::customizeContext(); + OculusBaseDisplayPlugin::customizeContext(); enableVsync(false); } From 1cbe6c446301c5208a37b985ba9752a1a19c1e98 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 4 Nov 2015 13:59:37 -0800 Subject: [PATCH 0702/1003] don't attempt to grab particles or zones --- examples/controllers/handControllerGrab.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 2d82896e26..afe72b530a 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -452,6 +452,14 @@ function MyController(hand) { continue; } + if (propsForCandidate.type == 'ParticleEffect') { + continue; + } + + if (propsForCandidate.type == 'Zone') { + continue; + } + if (propsForCandidate.locked && !grabbableDataForCandidate.wantsTrigger) { continue; } From 8b20a3c90368ec6f26697d147c7f871b6744252d Mon Sep 17 00:00:00 2001 From: Alan Z Date: Wed, 4 Nov 2015 14:41:11 -0800 Subject: [PATCH 0703/1003] Created playaSpawner and fixed cleanup issue --- examples/playa/playaSpawner.js | 142 +++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 examples/playa/playaSpawner.js diff --git a/examples/playa/playaSpawner.js b/examples/playa/playaSpawner.js new file mode 100644 index 0000000000..61101a0e62 --- /dev/null +++ b/examples/playa/playaSpawner.js @@ -0,0 +1,142 @@ +// dustSetSpawner.js +// examples +// +// Created by Eric Levin on 9/2/15 +// Copyright 2015 High Fidelity, Inc. +// +// Spawns a set with blocks and a desert-y ground. When blocks (or anything else is thrown), dust particles will kick up at the point the object hits the ground +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ + + +map = function(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} + +orientationOf = function(vector) { + var Y_AXIS = { + x: 0, + y: 1, + z: 0 + }; + var X_AXIS = { + x: 1, + y: 0, + z: 0 + }; + + var theta = 0.0; + + var RAD_TO_DEG = 180.0 / Math.PI; + var direction, yaw, pitch; + direction = Vec3.normalize(vector); + yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS); + pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS); + return Quat.multiply(yaw, pitch); +} + + +var ground, wall; +var boxes = []; +var dustSystems = []; +var ZERO_VEC = {x: 0, y: 0, z: 0}; + +Script.include("../libraries/utils.js"); + +function spawnGround() { + var groundModelURL = "https://hifi-public.s3.amazonaws.com/alan/Playa/Ground.fbx"; + var groundPosition = Vec3.sum(MyAvatar.position, {x: 0, y: -2, z: 0}); + ground = Entities.addEntity({ + type: "Model", + modelURL: groundModelURL, + shapeType: "box", + position: groundPosition, + dimensions: {x: 900, y: 0.82, z: 900}, + }); + // Script.addEventHandler(ground, "collisionWithEntity", entityCollisionWithGround); + +} + +/*function entityCollisionWithGround(ground, entity, collision) { + var dVelocityMagnitude = Vec3.length(collision.velocityChange); + var position = Entities.getEntityProperties(entity, "position").position; + var particleRadius = map(dVelocityMagnitude, 0.05, 3, 0.5, 2); + var speed = map(dVelocityMagnitude, 0.05, 3, 0.02, 0.09); + var displayTime = 400; + var orientationChange = orientationOf(collision.velocityChange); + var dustEffect = Entities.addEntity({ + type: "ParticleEffect", + name: "Dust-Puff", + position: position, + color: {red: 195, green: 170, blue: 185}, + lifespan: 3, + lifetime: 7,//displayTime/1000 * 2, //So we can fade particle system out gracefully + emitRate: 5, + emitSpeed: speed, + emitAcceleration: ZERO_VEC, + accelerationSpread: ZERO_VEC, + isEmitting: true, + polarStart: Math.PI/2, + polarFinish: Math.PI/2, + emitOrientation: orientationChange, + radiusSpread: 0.1, + radiusStart: particleRadius, + radiusFinish: particleRadius + particleRadius/2, + particleRadius: particleRadius, + alpha: 0.45, + alphaFinish: 0.001, + textures: "https://hifi-public.s3.amazonaws.com/alan/Playa/Particles/Particle-Sprite-Gen.png" + }); + + dustSystems.push(dustEffect); + + Script.setTimeout(function() { + var newRadius = 0.05; + Entities.editEntity(dustEffect, { + alpha: 0.0 + }); + }, displayTime); +}*/ + +function spawnBoxes() { + var boxModelURL = "https://hifi-public.s3.amazonaws.com/alan/Tower-Spawn/Stone-Block.fbx"; + var collisionSoundURL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/ToyWoodBlock.L.wav"; + var numBoxes = 200; + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + for (var i = 0; i < numBoxes; i++) { + var position = Vec3.sum(center, {x: Math.random() * numBoxes, y: Math.random() * 3, z: Math.random() * numBoxes }) + var box = Entities.addEntity({ + type: "Model", + modelURL: boxModelURL, + collisionSoundURL: collisionSoundURL, + shapeType: "box", + position: position, + collisionsWillMove: true, + dimensions: {x: 1, y: 2, z: 3}, + velocity: {x: 0, y: -.01, z: 0}, + gravity: {x: 0, y: -2.5 - Math.random() * 6, z: 0} + }); + + boxes.push(box); + } +} + +spawnGround(); +spawnBoxes(); + + +function cleanup() { + Entities.deleteEntity(ground); + boxes.forEach(function(box){ + Entities.deleteEntity(box); + }); + dustSystems.forEach(function(dustEffect) { + Entities.deleteEntity(dustEffect); + }) +} + +Script.scriptEnding.connect(cleanup); + + From b7840fdd6b48f4a921e72e54817187dc26f874f6 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 4 Nov 2015 15:01:55 -0800 Subject: [PATCH 0704/1003] explosion --- examples/helicopter/explodeHelicopter.js | 136 ++++++++++++++++++----- 1 file changed, 109 insertions(+), 27 deletions(-) diff --git a/examples/helicopter/explodeHelicopter.js b/examples/helicopter/explodeHelicopter.js index 98e806dbfd..efca1a91cc 100644 --- a/examples/helicopter/explodeHelicopter.js +++ b/examples/helicopter/explodeHelicopter.js @@ -1,10 +1,30 @@ -var partsURLS = [ - "https://s3.amazonaws.com/hifi-public/eric/models/blade.fbx", - "https://s3.amazonaws.com/hifi-public/eric/models/body.fbx", - "https://s3.amazonaws.com/hifi-public/eric/models/tail.fbx", -] +var explosionSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/eric/sounds/explosion.wav"); + +var partsURLS = [{ + url: "https://s3.amazonaws.com/hifi-public/eric/models/blade.fbx", + dimensions: { + x: 2, + y: 2, + z: 2 + } +}, { + url: "https://s3.amazonaws.com/hifi-public/eric/models/body.fbx", + dimensions: { + x: 2.2, + y: 2.98, + z: 7.96 + } +}, { + url: "https://s3.amazonaws.com/hifi-public/eric/models/tail.fbx", + dimensions: { + x: 1, + y: 1, + z: 1 + } +}]; var parts = []; +var emitters = []; var explodePosition; var helicopter; @@ -12,44 +32,103 @@ var entities = Entities.findEntities(MyAvatar.position, 2000); for (i = 0; i < entities.length; i++) { var name = Entities.getEntityProperties(entities[i], 'name').name; if (name === "Helicopter") { - helicopter = entities[i]; + var helicopter = entities[i]; explodeHelicopter(Entities.getEntityProperties(helicopter, 'position').position); } } function explodeHelicopter(explodePosition) { + Audio.playSound(explosionSound, { + position: explodePosition, + volume: 0.5 + }); Entities.deleteEntity(helicopter); for (var i = 0; i < partsURLS.length; i++) { + var position = Vec3.sum(explodePosition, { + x: 1, + y: 1, + z: 1 + }); var part = Entities.addEntity({ type: "Model", - modelURL: partsURLS[i], - position: explodePosition, + modelURL: partsURLS[i].url, + dimensions: partsURLS[i].dimensions, + position: position, shapeType: "box", - damping: 0 + collisionsWillMove: true, + damping: 0, + gravity: { + x: 0, + y: -9.6, + z: 0 + }, + velocity: { + x: Math.random(), + y: -10, + z: Math.random() + } }); + + var emitter = Entities.addEntity({ + type: "ParticleEffect", + name: "fire", + isEmitting: true, + textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png", + position: explodePosition, + emitRate: 100, + colorStart: { + red: 70, + green: 70, + blue: 137 + }, + color: { + red: 200, + green: 99, + blue: 42 + }, + colorFinish: { + red: 255, + green: 99, + blue: 32 + }, + radiusSpread: 0.2, + radiusStart: 0.3, + radiusEnd: 0.04, + particleRadius: 0.09, + radiusFinish: 0.0, + emitSpeed: 0.1, + speedSpread: 0.1, + alphaStart: 0.1, + alpha: 0.7, + alphaFinish: 0.1, + emitOrientation: Quat.fromPitchYawRollDegrees(-90, 0, 0), + emitDimensions: { + x: 1, + y: 1, + z: 0.1 + }, + polarFinish: Math.PI, + polarStart: 0, + + accelerationSpread: { + x: 0.1, + y: 0.01, + z: 0.1 + }, + lifespan: 1, + }); + emitters.push(emitter) parts.push(part); } Script.setTimeout(function() { - parts.forEach(function(part) { - var naturalDimensions = Entities.getEntityProperties(part, "naturalDimensions").naturalDimensions; - Entities.editEntity(part, { - dimensions: naturalDimensions, - gravity: { - x: 0, - y: -9.6, - z: 0 - }, - velocity: { - x: Math.random(), - y: -10, - z: Math.random() - }, - collisionsWillMove: true - }); - }); - }, 1000); + var pos = Entities.getEntityProperties(parts[1], "position").position; + Entities.editEntity(emitters[0], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})}); + Entities.editEntity(emitters[1], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})}); + Entities.editEntity(emitters[2], {position: Vec3.sum(pos, {x: Math.random(), y: Math.random(), z: Math.random()})}); + }, 5000) + } @@ -57,6 +136,9 @@ function cleanup() { parts.forEach(function(part) { Entities.deleteEntity(part); }); + emitters.forEach(function(emitter){ + Entities.deleteEntity(emitter); + }) } Script.scriptEnding.connect(cleanup); \ No newline at end of file From d46d521d81e65b62a47b13740cd94a8438a6e020 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 4 Nov 2015 15:10:23 -0800 Subject: [PATCH 0705/1003] Added #else branch to #if 0 --- interface/src/devices/3DConnexionClient.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/devices/3DConnexionClient.h b/interface/src/devices/3DConnexionClient.h index b6fa6a37c3..7435739bca 100755 --- a/interface/src/devices/3DConnexionClient.h +++ b/interface/src/devices/3DConnexionClient.h @@ -218,7 +218,7 @@ public: void handleAxisEvent(); }; -#endif +#else // #if 0 #include #include @@ -235,4 +235,6 @@ public slots: void toggleConnexion(bool shouldEnable) {}; }; +#endif + #endif // defined(hifi_3DConnexionClient_h) From 20d1035821b83b5d4d29bbf3ca94d4db09a15498 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 15:50:21 -0800 Subject: [PATCH 0706/1003] Fixing step yaw to move by consistent amounts, be 'tappable' --- .../resources/controllers/keyboardMouse.json | 35 +++++++++++++++---- interface/src/Application.cpp | 2 +- .../controllers/impl/filters/PulseFilter.cpp | 15 ++++++-- .../controllers/impl/filters/PulseFilter.h | 7 ++-- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index f99472b0e9..4a68ca0d20 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -12,28 +12,49 @@ { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, - { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, + { "comment" : "Mouse turn need to be small continuous increments", + "from": { "makeAxis" : [ + [ "Keyboard.MouseMoveLeft" ], + [ "Keyboard.MouseMoveRight" ] + ] + }, "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseButton" ], "to": "Actions.StepYaw", "filters": [ "constrainToInteger", - { "type": "pulse", "interval": 0.5 }, - { "type": "scale", "scale": 15 } + { "type": "pulse", "interval": 0.2 }, + { "type": "scale", "scale": 22.5 } + ] + }, + + { "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint", + "from": { "makeAxis" : [ + [ "Keyboard.TouchpadLeft" ], + [ "Keyboard.TouchpadRight" ] + ] + }, + "when": [ "Application.InHMD", "Application.ComfortMode" ], + "to": "Actions.StepYaw", + "filters": + [ + "constrainToInteger", + { "type": "pulse", "interval": 0.2 }, + { "type": "scale", "scale": 22.5 } ] }, { "from": { "makeAxis" : [ - ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], - ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ["Keyboard.A", "Keyboard.Left" ], + ["Keyboard.D", "Keyboard.Right"] ] }, "when": [ "Application.InHMD", "Application.ComfortMode" ], "to": "Actions.StepYaw", "filters": [ - { "type": "pulse", "interval": 0.5 }, - { "type": "scale", "scale": 15 } + { "type": "pulse", "interval": 0.5, "resetOnZero": true }, + { "type": "scale", "scale": 22.5 } ] }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d31d9de4a0..da92605cb8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2795,7 +2795,7 @@ void Application::update(float deltaTime) { float timeFactor = EXPECTED_FRAME_RATE * deltaTime; 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); + myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW)); } } myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp b/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp index f4e1f04791..6ef9d40802 100644 --- a/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp @@ -13,7 +13,7 @@ using namespace controller; - +const float PulseFilter::DEFAULT_LAST_EMIT_TIME = -::std::numeric_limits::max(); float PulseFilter::apply(float value) const { float result = 0.0f; @@ -25,13 +25,22 @@ float PulseFilter::apply(float value) const { _lastEmitTime = now; result = value; } + } else if (_resetOnZero) { + _lastEmitTime = DEFAULT_LAST_EMIT_TIME; } return result; } bool PulseFilter::parseParameters(const QJsonValue& parameters) { - static const QString JSON_MIN = QStringLiteral("interval"); - return parseSingleFloatParameter(parameters, JSON_MIN, _interval); + static const QString JSON_INTERVAL = QStringLiteral("interval"); + static const QString JSON_RESET = QStringLiteral("resetOnZero"); + if (parameters.isObject()) { + auto obj = parameters.toObject(); + if (obj.contains(JSON_RESET)) { + _resetOnZero = obj[JSON_RESET].toBool(); + } + } + return parseSingleFloatParameter(parameters, JSON_INTERVAL, _interval); } diff --git a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h index 2512b479cf..dbe2eba81b 100644 --- a/libraries/controllers/src/controllers/impl/filters/PulseFilter.h +++ b/libraries/controllers/src/controllers/impl/filters/PulseFilter.h @@ -21,14 +21,15 @@ 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; + static const float DEFAULT_LAST_EMIT_TIME; + mutable float _lastEmitTime { DEFAULT_LAST_EMIT_TIME }; + bool _resetOnZero { false }; + float _interval { 1.0f }; }; } From 4e5b806934a188599aa43e0d606c7bae7260fc93 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 4 Nov 2015 16:05:29 -0800 Subject: [PATCH 0707/1003] particle effect entities no longer intersect with rays --- libraries/entities/src/EntityTreeElement.cpp | 4 +++- libraries/entities/src/ParticleEffectEntityItem.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 57f49f2354..7ada138d02 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -18,6 +18,7 @@ #include "EntityItemProperties.h" #include "EntityTree.h" #include "EntityTreeElement.h" +#include "EntityTypes.h" EntityTreeElement::EntityTreeElement(unsigned char* octalCode) : OctreeElement() { init(octalCode); @@ -591,7 +592,8 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - if (localDistance < distance) { + // Never intersect with particle effect entities + if (localDistance < distance && EntityTypes::getEntityTypeName(entity->getType()) != "ParticleEffect") { distance = localDistance; face = localFace; surfaceNormal = localSurfaceNormal; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 6560a7bc33..e3c5cd895a 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -215,6 +215,8 @@ public: _additiveBlending = additiveBlending; } + virtual bool supportsDetailedRayIntersection() const { return false; } + protected: bool isAnimatingSomething() const; From 69d29bbbbb921eebe8dc481d997e3aa0e7d54d6b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 16:18:55 -0800 Subject: [PATCH 0708/1003] Creating recording library --- libraries/recording/CMakeLists.txt | 9 +++++++ libraries/recording/src/recording/Forward.h | 28 +++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 libraries/recording/CMakeLists.txt create mode 100644 libraries/recording/src/recording/Forward.h diff --git a/libraries/recording/CMakeLists.txt b/libraries/recording/CMakeLists.txt new file mode 100644 index 0000000000..a0beae4496 --- /dev/null +++ b/libraries/recording/CMakeLists.txt @@ -0,0 +1,9 @@ +set(TARGET_NAME recording) + +# 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) + +GroupSources("src/recording") diff --git a/libraries/recording/src/recording/Forward.h b/libraries/recording/src/recording/Forward.h new file mode 100644 index 0000000000..83a89da847 --- /dev/null +++ b/libraries/recording/src/recording/Forward.h @@ -0,0 +1,28 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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_Recording_Forward_h +#define hifi_Recording_Forward_h + +#include +#include + +namespace recording { + +// A recording of some set of state from the application, usually avatar +// data + audio for a single person +class Clip; + +// An interface for interacting with clips, creating them by recording or +// playing them back. Also serialization to and from files / network sources +class Deck; + +} + +#endif From adb9118dc1df77517a591f8170f6c91f77142b9b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 4 Nov 2015 16:21:02 -0800 Subject: [PATCH 0709/1003] don't set release velocity --- examples/controllers/handControllerGrab.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index afe72b530a..1a5b28b82c 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -903,9 +903,9 @@ function MyController(hand) { // the action will tend to quickly bring an object's velocity to zero. now that // the action is gone, set the objects velocity to something the holder might expect. - Entities.editEntity(this.grabbedEntity, { - velocity: this.grabbedVelocity - }); + // Entities.editEntity(this.grabbedEntity, { + // velocity: this.grabbedVelocity + // }); this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; From 05a977e31bc87ea96190c1fd0f359d08e587f3ee Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 4 Nov 2015 16:26:30 -0800 Subject: [PATCH 0710/1003] get rid of code that computes release velocity --- examples/controllers/handControllerGrab.js | 29 +--------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 1a5b28b82c..b0721de119 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -178,12 +178,11 @@ function MyController(hand) { this.actionID = null; // action this script created... this.grabbedEntity = null; // on this entity. - this.grabbedVelocity = ZERO_VEC; // rolling average of held object's velocity this.state = STATE_OFF; this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; - + this.offsetPosition = { x: 0.0, y: 0.0, z: 0.0 }; this.offsetRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; @@ -595,7 +594,6 @@ function MyController(hand) { var deltaPosition = Vec3.subtract(newObjectPosition, this.currentObjectPosition); // meters var now = Date.now(); var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - this.computeReleaseVelocity(deltaPosition, deltaTime, false); this.currentObjectPosition = newObjectPosition; this.currentObjectTime = now; @@ -715,7 +713,6 @@ function MyController(hand) { var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds - this.computeReleaseVelocity(deltaPosition, deltaTime, true); this.currentHandControllerTipPosition = handControllerPosition; this.currentObjectTime = now; @@ -867,23 +864,6 @@ function MyController(hand) { Entities.callEntityMethod(entityID, "stopTouch"); }; - this.computeReleaseVelocity = function(deltaPosition, deltaTime, useMultiplier) { - if (deltaTime > 0.0 && !vec3equal(deltaPosition, ZERO_VEC)) { - var grabbedVelocity = Vec3.multiply(deltaPosition, 1.0 / deltaTime); - // don't update grabbedVelocity if the trigger is off. the smoothing of the trigger - // value would otherwise give the held object time to slow down. - if (this.triggerSqueezed()) { - this.grabbedVelocity = - Vec3.sum(Vec3.multiply(this.grabbedVelocity, (1.0 - NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)), - Vec3.multiply(grabbedVelocity, NEAR_GRABBING_VELOCITY_SMOOTH_RATIO)); - } - - if (useMultiplier) { - this.grabbedVelocity = Vec3.multiply(this.grabbedVelocity, RELEASE_VELOCITY_MULTIPLIER); - } - } - }; - this.release = function() { if(this.hand !== disabledHand){ @@ -901,13 +881,6 @@ function MyController(hand) { this.deactivateEntity(this.grabbedEntity); - // the action will tend to quickly bring an object's velocity to zero. now that - // the action is gone, set the objects velocity to something the holder might expect. - // Entities.editEntity(this.grabbedEntity, { - // velocity: this.grabbedVelocity - // }); - - this.grabbedVelocity = ZERO_VEC; this.grabbedEntity = null; this.actionID = null; this.setState(STATE_OFF); From eac73671e0131619b6227a285189a9d8e7174084 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Wed, 4 Nov 2015 17:40:35 -0800 Subject: [PATCH 0711/1003] Added some documentation and examples for the new mapping system --- examples/controllers/controllerMappings.js | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) 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..a44833f80a --- /dev/null +++ b/examples/controllers/controllerMappings.js @@ -0,0 +1,98 @@ + +// +// 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 +// + +// This allows to change the input mapping making a sense of how the new mapping works + + +/* +This function returns a JSON body. It's in charge to modify the mouse/keyboard mapping. The keyboard inputs are mapped directly to actions. +If this new mapping contains inputs which are defined in the standard mapping, these will overwrite the old ones(Keyboard.W, Keyboard.RightMouseButton). +If this new mapping contains inputs which are not defined in the standard, these will be added to the mapping(Keyboard.M). +*/ +myFirstMapping = function() { +return { + "name": "example", + "channels": [ + { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.M", "to": "Actions.YAW_LEFT" }, + + { "from": "Keyboard.RightMouseButton", "to": "Actions.YAW_RIGHT" } + ] +} +} + +/* +This JSON is in charge to modify the Standard controller mapping. +The standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) +These new inputs will overwrite the standard ones. +It's possible to find all the standard inputs (and their mapping) into standard.json +To try the mySecondMapping effect you need a controller, not the keyboard. +*/ +mySecondMapping = function() { +return { + "name": "example2", + "channels": [ + { "from": "Standard.LY", "to": "Actions.Yaw" }, + { "from": "Standard.LX", "to": "Actions.Yaw" }, + ] +} +} + + +var tryFirst = true; +var mapping; +if(tryFirst){ + var myFirstMappingJSON = myFirstMapping(); + print('myfirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); + mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); +}else{ + var mySecondMappingJSON = mySecondMapping(); + print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); + mapping = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); +} +mapping.enable(); + + + + +/* +//-----------------some info prints----------------------- +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]); +}); +*/ + + +Controller.hardwareChanged.connect(function () { + 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("-------------------------------------------------------------------"); +}); + + +Script.scriptEnding.connect(function () { + mapping.disable(); +}); \ No newline at end of file From f4c610a0f49cf6eb95bc9ae750bee77a6488762b Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 4 Nov 2015 18:21:04 -0800 Subject: [PATCH 0712/1003] Tuning the standard mapping for tbringin vertical axis --- interface/resources/controllers/standard.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5de188c0b6..bd2e0c437f 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -15,7 +15,7 @@ }, { "from": "Standard.RX", "to": "Actions.Yaw" }, { "from": "Standard.RY", "when": "!Application.InHMD", "to": "Actions.Pitch" }, - + { "from": "Standard.RY", "when": "Application.InHMD", "filters": "invert", "to": "Actions.TranslateY" }, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, @@ -35,3 +35,6 @@ } + + + From 8d16f60b15ee192e07bfffa2bbc8b4c556e6b920 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 09:02:39 -0800 Subject: [PATCH 0713/1003] Removed hardcoded define HAVE_3DCONNEXIONCLIENT This should be set from cmake, right? --- libraries/input-plugins/src/input-plugins/SpacemouseManager.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index 01df038ccb..e444b73d9f 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -11,8 +11,6 @@ #ifndef hifi_SpacemouseManager_h #define hifi_SpacemouseManager_h -#define HAVE_3DCONNEXIONCLIENT - #include #include #include From 2318e48fa8dfce0e0ad2ecd1cf108693a57998f5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 09:43:41 -0800 Subject: [PATCH 0714/1003] AvatarRig: fix for crash when index was -1 --- libraries/animation/src/AvatarRig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 4dbf5207b1..b72e15a0ce 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -13,7 +13,7 @@ /// Updates the state of the joint at the specified index. void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) { - if (index < 0 && index >= _jointStates.size()) { + if (index < 0 || index >= _jointStates.size()) { return; // bail } JointState& state = _jointStates[index]; From 4a3b434fd48f7186988d4350e72fa8df92a65044 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 5 Nov 2015 10:35:02 -0800 Subject: [PATCH 0715/1003] change grabbing-non-colliding to trigger. fix whiteboard and some other scripts --- examples/controllers/handControllerGrab.js | 75 ++++++++++--------- .../whiteboard/colorSelectorEntityScript.js | 4 +- .../whiteboard/eraseBoardEntityScript.js | 4 +- .../whiteboard/whiteboardEntityScript.js | 8 +- .../painting/whiteboard/whiteboardSpawner.js | 5 +- examples/toybox/lights/lightSwitch.js | 7 +- unpublishedScripts/basketballsResetter.js | 4 +- unpublishedScripts/hiddenEntityReset.js | 4 +- unpublishedScripts/masterReset.js | 5 +- unpublishedScripts/targetsResetter.js | 4 +- 10 files changed, 66 insertions(+), 54 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index b0721de119..6ea08e5a43 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -101,10 +101,10 @@ var STATE_DISTANCE_HOLDING = 2; var STATE_CONTINUE_DISTANCE_HOLDING = 3; var STATE_NEAR_GRABBING = 4; var STATE_CONTINUE_NEAR_GRABBING = 5; -var STATE_NEAR_GRABBING_NON_COLLIDING = 6; -var STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING = 7; -var STATE_FAR_GRABBING_NON_COLLIDING = 8; -var STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING = 9; +var STATE_NEAR_TRIGGER = 6; +var STATE_CONTINUE_NEAR_TRIGGER = 7; +var STATE_FAR_TRIGGER = 8; +var STATE_CONTINUE_FAR_TRIGGER = 9; var STATE_RELEASE = 10; @@ -122,14 +122,14 @@ function stateToName(state) { return "near_grabbing"; case STATE_CONTINUE_NEAR_GRABBING: return "continue_near_grabbing"; - case STATE_NEAR_GRABBING_NON_COLLIDING: - return "near_grabbing_non_colliding"; - case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING: - return "continue_near_grabbing_non_colliding"; - case STATE_FAR_GRABBING_NON_COLLIDING: - return "far_grabbing_non_colliding"; - case STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING: - return "continue_far_grabbing_non_colliding"; + case STATE_NEAR_TRIGGER: + return "near_trigger"; + case STATE_CONTINUE_NEAR_TRIGGER: + return "continue_near_trigger"; + case STATE_FAR_TRIGGER: + return "far_trigger"; + case STATE_CONTINUE_FAR_TRIGGER: + return "continue_far_trigger"; case STATE_RELEASE: return "release"; } @@ -212,17 +212,17 @@ function MyController(hand) { case STATE_CONTINUE_NEAR_GRABBING: this.continueNearGrabbing(); break; - case STATE_NEAR_GRABBING_NON_COLLIDING: - this.nearGrabbingNonColliding(); + case STATE_NEAR_TRIGGER: + this.nearTrigger(); break; - case STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING: - this.continueNearGrabbingNonColliding(); + case STATE_CONTINUE_NEAR_TRIGGER: + this.continueNearTrigger(); break; - case STATE_FAR_GRABBING_NON_COLLIDING: - this.farGrabbingNonColliding(); + case STATE_FAR_TRIGGER: + this.farTrigger(); break; - case STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING: - this.continueFarGrabbingNonColliding(); + case STATE_CONTINUE_FAR_TRIGGER: + this.continueFarTrigger(); break; case STATE_RELEASE: this.release(); @@ -394,7 +394,7 @@ function MyController(hand) { // the hand is very close to the intersected object. go into close-grabbing mode. if (grabbableData.wantsTrigger) { this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + this.setState(STATE_NEAR_TRIGGER); return; } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; @@ -411,7 +411,7 @@ function MyController(hand) { return; } else if (grabbableData.wantsTrigger) { this.grabbedEntity = intersection.entityID; - this.setState(STATE_FAR_GRABBING_NON_COLLIDING); + this.setState(STATE_FAR_TRIGGER); return; } } @@ -483,7 +483,7 @@ function MyController(hand) { return; } if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + this.setState(STATE_NEAR_TRIGGER); return; } else if (!props.locked) { this.setState(STATE_NEAR_GRABBING); @@ -538,6 +538,7 @@ function MyController(hand) { this.continueDistanceHolding = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } @@ -631,6 +632,7 @@ function MyController(hand) { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } @@ -698,6 +700,7 @@ function MyController(hand) { this.continueNearGrabbing = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } @@ -733,9 +736,10 @@ function MyController(hand) { } }; - this.nearGrabbingNonColliding = function() { + this.nearTrigger = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); return; } if (this.hand === RIGHT_HAND) { @@ -743,13 +747,14 @@ function MyController(hand) { } else { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } - Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding"); - this.setState(STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING); + Entities.callEntityMethod(this.grabbedEntity, "startNearTrigger"); + this.setState(STATE_CONTINUE_NEAR_TRIGGER); }; - this.farGrabbingNonColliding = function() { + this.farTrigger = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); return; } @@ -758,22 +763,24 @@ function MyController(hand) { } else { Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); } - Entities.callEntityMethod(this.grabbedEntity, "startFarGrabNonColliding"); - this.setState(STATE_CONTINUE_FAR_GRABBING_NON_COLLIDING); + Entities.callEntityMethod(this.grabbedEntity, "startFarTrigger"); + this.setState(STATE_CONTINUE_FAR_TRIGGER); }; - this.continueNearGrabbingNonColliding = function() { + this.continueNearTrigger = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); return; } - Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); + Entities.callEntityMethod(this.grabbedEntity, "continueNearTrigger"); }; - this.continueFarGrabbingNonColliding = function() { + this.continueFarTrigger = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopNearTrigger"); return; } @@ -789,12 +796,13 @@ function MyController(hand) { this.lastPickTime = now; if (intersection.entityID != this.grabbedEntity) { this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "stopFarTrigger"); return; } } this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - Entities.callEntityMethod(this.grabbedEntity, "continueFarGrabbingNonColliding"); + Entities.callEntityMethod(this.grabbedEntity, "continueFarTrigger"); }; _this.allTouchedIDs = {}; @@ -876,7 +884,6 @@ function MyController(hand) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); } - Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); } this.deactivateEntity(this.grabbedEntity); diff --git a/examples/painting/whiteboard/colorSelectorEntityScript.js b/examples/painting/whiteboard/colorSelectorEntityScript.js index f1105604c7..3c07527d02 100644 --- a/examples/painting/whiteboard/colorSelectorEntityScript.js +++ b/examples/painting/whiteboard/colorSelectorEntityScript.js @@ -20,7 +20,7 @@ ColorSelector.prototype = { - startFarGrabNonColliding: function() { + startFarTrigger: function() { this.selectColor(); }, @@ -46,4 +46,4 @@ // entity scripts always need to return a newly constructed object of our type return new ColorSelector(); -}); \ No newline at end of file +}); diff --git a/examples/painting/whiteboard/eraseBoardEntityScript.js b/examples/painting/whiteboard/eraseBoardEntityScript.js index 14679c625b..dd36242a6d 100644 --- a/examples/painting/whiteboard/eraseBoardEntityScript.js +++ b/examples/painting/whiteboard/eraseBoardEntityScript.js @@ -20,7 +20,7 @@ BoardEraser.prototype = { - startFarGrabNonColliding: function() { + startFarTrigger: function() { this.eraseBoard(); }, @@ -42,4 +42,4 @@ // entity scripts always need to return a newly constructed object of our type return new BoardEraser(); -}); \ No newline at end of file +}); diff --git a/examples/painting/whiteboard/whiteboardEntityScript.js b/examples/painting/whiteboard/whiteboardEntityScript.js index c10a8c23fe..d2b2c386ef 100644 --- a/examples/painting/whiteboard/whiteboardEntityScript.js +++ b/examples/painting/whiteboard/whiteboardEntityScript.js @@ -43,7 +43,7 @@ this.hand = LEFT_HAND; }, - startFarGrabNonColliding: function() { + startFarTrigger: function() { if (this.painting) { return; } @@ -62,7 +62,7 @@ }); }, - continueFarGrabbingNonColliding: function() { + continueFarTrigger: function() { var handPosition = this.getHandPosition(); var pickRay = { origin: handPosition, @@ -183,7 +183,7 @@ }, - releaseGrab: function() { + stopFarTrigger: function() { if(this.hand !== this.whichHand) { return; } @@ -249,4 +249,4 @@ // entity scripts always need to return a newly constructed object of our type return new Whiteboard(); -}); \ No newline at end of file +}); diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 701112e1a8..3411061074 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -74,6 +74,9 @@ var drawingSurface = Entities.addEntity({ userData: JSON.stringify({ color: { currentColor: colors[0] + }, + "grabbableKey": { + wantsTrigger:true } }) @@ -211,4 +214,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); \ No newline at end of file +// Script.scriptEnding.connect(cleanup); diff --git a/examples/toybox/lights/lightSwitch.js b/examples/toybox/lights/lightSwitch.js index fbd314a9af..5d6497fccb 100644 --- a/examples/toybox/lights/lightSwitch.js +++ b/examples/toybox/lights/lightSwitch.js @@ -33,7 +33,7 @@ this.toggleLights(); }, - startNearGrabNonColliding: function () { + startNearTrigger: function () { this.toggleLights(); }, @@ -61,7 +61,8 @@ setEntityCustomData(this.resetKey, this.entityID, { on: on, type: lightType, - resetMe: true + resetMe: true, + grabbableKey: {wantsTrigger:true} }); }, @@ -89,4 +90,4 @@ // entity scripts always need to return a newly constructed object of our type return new LightSwitch(); -}); \ No newline at end of file +}); diff --git a/unpublishedScripts/basketballsResetter.js b/unpublishedScripts/basketballsResetter.js index 6335312d57..4d02296e8e 100644 --- a/unpublishedScripts/basketballsResetter.js +++ b/unpublishedScripts/basketballsResetter.js @@ -17,7 +17,7 @@ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Resetter.prototype = { - startNearGrabNonColliding: function() { + startNearTrigger: function() { this.resetObjects(); }, @@ -108,4 +108,4 @@ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; }; return new Resetter(); -}); \ No newline at end of file +}); diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index f56c705ce4..d343c52497 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -39,7 +39,7 @@ }, - startNearGrabNonColliding: function() { + startNearTrigger: function() { this.triggerReset(); }, @@ -1259,4 +1259,4 @@ }; // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); \ No newline at end of file +}); diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index b138db163d..364807f42f 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -247,7 +247,8 @@ MasterReset = function() { resetMe: true }, grabbableKey: { - grabbable: false + grabbable: false, + wantsTrigger:true } }) }); @@ -1237,4 +1238,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; \ No newline at end of file +}; diff --git a/unpublishedScripts/targetsResetter.js b/unpublishedScripts/targetsResetter.js index a522c19593..326c0ce1be 100644 --- a/unpublishedScripts/targetsResetter.js +++ b/unpublishedScripts/targetsResetter.js @@ -17,7 +17,7 @@ Resetter.prototype = { - startNearGrabNonColliding: function() { + startNearTrigger: function() { this.resetObjects(); }, @@ -125,4 +125,4 @@ }; return new Resetter(); -}); \ No newline at end of file +}); From c898d9a47483609d1fabe68367792291343082b6 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 5 Nov 2015 10:48:16 -0800 Subject: [PATCH 0716/1003] set wantstrigger correctly on lightswitch --- examples/toybox/lights/lightSwitch.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/examples/toybox/lights/lightSwitch.js b/examples/toybox/lights/lightSwitch.js index 5d6497fccb..6051b7c0ae 100644 --- a/examples/toybox/lights/lightSwitch.js +++ b/examples/toybox/lights/lightSwitch.js @@ -15,35 +15,35 @@ /*global LightSwitch */ -(function () { +(function() { var _this; var utilitiesScript = Script.resolvePath("../../libraries/utils.js"); Script.include(utilitiesScript); - LightSwitch = function () { + LightSwitch = function() { _this = this; this.switchSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav"); }; LightSwitch.prototype = { - clickReleaseOnEntity: function (entityID, mouseEvent) { + clickReleaseOnEntity: function(entityID, mouseEvent) { if (!mouseEvent.isLeftButton) { return; } this.toggleLights(); }, - startNearTrigger: function () { + startNearTrigger: function() { this.toggleLights(); }, - toggleLights: function () { + toggleLights: function() { var lightData = getEntityCustomData(this.resetKey, this.entityID, {}); var on = !lightData.on; var lightType = lightData.type; var lights = Entities.findEntities(this.position, 20); - lights.forEach(function (light) { + lights.forEach(function(light) { var type = getEntityCustomData(_this.resetKey, light, {}).type; if (type === lightType && JSON.stringify(light) !== JSON.stringify(_this.entityID)) { Entities.editEntity(light, { @@ -61,12 +61,15 @@ setEntityCustomData(this.resetKey, this.entityID, { on: on, type: lightType, - resetMe: true, - grabbableKey: {wantsTrigger:true} + resetMe: true + }); + + setEntityCustomData('grabbableKey', this.entityID, { + wantsTrigger: true }); }, - flipSwitch: function () { + flipSwitch: function() { var rotation = Entities.getEntityProperties(this.entityID, "rotation").rotation; var axis = { x: 0, @@ -80,7 +83,7 @@ rotation: rotation }); }, - preload: function (entityID) { + preload: function(entityID) { this.entityID = entityID; this.resetKey = "resetMe"; //The light switch is static, so just cache its position once @@ -90,4 +93,4 @@ // entity scripts always need to return a newly constructed object of our type return new LightSwitch(); -}); +}); \ No newline at end of file From 50e8f49f5241820f364e9beeb783ab6731d160af Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 10:54:50 -0800 Subject: [PATCH 0717/1003] SpacemouseManager Moved stub init into cpp file. This should fix the windows build. For some reason the linker was having a problem resolving this. --- .../input-plugins/src/input-plugins/SpacemouseManager.cpp | 3 +++ libraries/input-plugins/src/input-plugins/SpacemouseManager.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp index 9c1307a18e..43e6ee48a8 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -130,6 +130,9 @@ void SpacemouseManager::ManagerFocusOutEvent() { instance->focusOutEvent(); } +void SpacemouseManager::init() { +} + #ifdef HAVE_3DCONNEXIONCLIENT #ifdef Q_OS_WIN diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index e444b73d9f..08ac954c94 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -25,7 +25,7 @@ class SpacemouseManager : public QObject { public: static SpacemouseManager& getInstance(); void ManagerFocusOutEvent(); - void init() {}; + void init(); void destroy() {}; bool Is3dmouseAttached() { return false; }; public slots: From 50370479e475fa1c3f54695a674e230f21c905e4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 11:03:32 -0800 Subject: [PATCH 0718/1003] Application.h: coding standard fixes --- interface/src/Application.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 5a5d5a015c..39e3879707 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -414,7 +414,7 @@ private: bool _dependencyManagerIsSetup; - OffscreenGlCanvas* _offscreenContext {nullptr}; + OffscreenGlCanvas* _offscreenContext { nullptr }; DisplayPluginPointer _displayPlugin; InputPluginList _activeInputPlugins; @@ -548,7 +548,7 @@ private: quint64 _lastSimsPerSecondUpdate = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; - bool _isGLInitialized {false}; + bool _isGLInitialized { false }; }; #endif // hifi_Application_h From 20d95080f1c0a6a252e1ee7e4364451c93db672c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 5 Nov 2015 12:03:45 -0800 Subject: [PATCH 0719/1003] IK fix for avatars exported from Blender This should fix the issue with the hips moving erratically when arm IK is enabled. The main issue is that the IK system assumed that the "Hips" joint was the root of the skeleton. For Blender avatar this is not the case as it inserts an "Armature" node at the root instead. --- libraries/animation/src/AnimInverseKinematics.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 42e9472819..6355426dae 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -156,11 +156,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (pivotIndex == -1) { + if (pivotIndex == -1 || pivotIndex == _hipsIndex) { continue; } int pivotsParentIndex = _skeleton->getParentIndex(pivotIndex); - if (pivotsParentIndex == -1) { + if (pivotsParentIndex == -1 || pivotIndex == _hipsIndex) { // TODO?: handle case where tip's parent is root? continue; } @@ -173,7 +173,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(i); - if (parentIndex != -1) { + if (parentIndex != -1 && parentIndex != _hipsIndex) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -295,7 +295,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (parentIndex != -1) { + if (parentIndex != -1 && parentIndex != _hipsIndex) { const glm::quat& targetRotation = target.getRotation(); // compute tip's new parent-relative rotation // Q = Qp * q --> q' = Qp^ * Q From 8f65ee87bcc77a6cbecb9b287a118086e9415695 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 5 Nov 2015 13:19:29 -0800 Subject: [PATCH 0720/1003] Clean up script, go away when putting HMD (not just on startup), and clear stopper properly. --- examples/away.js | 55 ++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/examples/away.js b/examples/away.js index 516365218c..8cc3790200 100644 --- a/examples/away.js +++ b/examples/away.js @@ -32,6 +32,7 @@ function playAwayAnimation() { } if (stopper) { Script.clearTimeout(stopper); + stopper = false; MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment } awayAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateAway, null); @@ -79,8 +80,8 @@ hideOverlay(); // MAIN CONTROL var wasMuted, isAway; -function goAway(event) { - if (isAway || event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) +function goAway() { + if (isAway) { return; } isAway = true; @@ -93,24 +94,36 @@ function goAway(event) { playAwayAnimation(); // animation is still seen by others showOverlay(); } -function switchActiveState(event) { - if (!isAway || event.isAutoRepeat) { - if (event.text === '.') { - goAway(event); - } - } else { - isAway = false; - print('going "active"'); - if (!wasMuted) { - AudioDevice.toggleMute(); - } - MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. - stopAwayAnimation(); - hideOverlay(); +function goActive() { + if (!isAway) { + return; } + isAway = false; + print('going "active"'); + if (!wasMuted) { + AudioDevice.toggleMute(); + } + MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. + stopAwayAnimation(); + hideOverlay(); } -Controller.keyPressEvent.connect(switchActiveState); -Script.scriptEnding.connect(switchActiveState); -if (HMD.active) { - goAway({}); // give a dummy event object -} +Script.scriptEnding.connect(goActive); +Controller.keyPressEvent.connect(function (event) { + if (event.isAutoRepeat) { // isAutoRepeat is true when held down (or when Windows feels like it) + return; + } + if (!isAway && (event.text === '.')) { + goAway(); + } else { + goActive(); + } +}); +var wasHmdActive = false; +Script.update.connect(function () { + if (HMD.active !== wasHmdActive) { + wasHmdActive = !wasHmdActive; + if (wasHmdActive) { + goAway(); + } + } +}); From c4194f23597850945b68b81d3a17b729d49200b4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 5 Nov 2015 13:26:23 -0800 Subject: [PATCH 0721/1003] don't try to grab polylines. set wantTrigger on various whiteboard parts --- examples/controllers/handControllerGrab.js | 4 ++++ examples/painting/whiteboard/whiteboardSpawner.js | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 6ea08e5a43..5035f2e5a4 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -455,6 +455,10 @@ function MyController(hand) { continue; } + if (propsForCandidate.type == 'PolyLine') { + continue; + } + if (propsForCandidate.type == 'Zone') { continue; } diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 3411061074..53bb7158d5 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -75,8 +75,8 @@ var drawingSurface = Entities.addEntity({ color: { currentColor: colors[0] }, - "grabbableKey": { - wantsTrigger:true + grabbableKey: { + wantsTrigger: true } }) @@ -156,7 +156,10 @@ function setUp() { color: { currentColor: colors[0] }, - colorIndicator: colorIndicatorBox + colorIndicator: colorIndicatorBox, + grabbableKey: { + wantsTrigger: true + } }) }); @@ -189,7 +192,8 @@ function setUp() { script: scriptURL, userData: JSON.stringify({ whiteboard: drawingSurface, - colorIndicator: colorIndicatorBox + colorIndicator: colorIndicatorBox, + grabbableKey: {wantsTrigger: true} }) }); colorBoxes.push(colorBox); From 07b3597ae363f79202a5f59cbf3de353f29c648b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 31 Aug 2015 16:53:30 +0200 Subject: [PATCH 0722/1003] Fix recording scripts --- examples/acScripts/ControlACs.js | 22 ++- examples/acScripts/ControlledAC.js | 202 ++++++++++++------------ examples/acScripts/PlayRecordingOnAC.js | 47 +++--- examples/utilities/record/recorder.js | 7 +- libraries/avatars/src/Player.cpp | 11 +- libraries/avatars/src/Recorder.h | 1 - 6 files changed, 146 insertions(+), 144 deletions(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index 45c498dc7f..403c0878cb 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -16,11 +16,11 @@ var NUM_AC = 3; // This is the number of AC. Their ID need to be unique and betw var NAMES = new Array("Craig", "Clement", "Jeff"); // ACs names ordered by IDs (Default name is "ACx", x = ID + 1)) // Those variables MUST be common to every scripts -var controlVoxelSize = 0.25; -var controlVoxelPosition = { x: 2000 , y: 0, z: 0 }; +var controlEntitySize = 0.25; +var controlEntityPosition = { x: 2000 , y: 0, z: 0 }; // Script. DO NOT MODIFY BEYOND THIS LINE. -Script.include("libraries/toolBars.js"); +Script.include("../libraries/toolBars.js"); var DO_NOTHING = 0; var PLAY = 1; @@ -138,16 +138,22 @@ function sendCommand(id, action) { return; } - if (id === toolBars.length - 1) { + if (id === (toolBars.length - 1)) { for (i = 0; i < NUM_AC; i++) { sendCommand(i, action); } return; } - - // TODO: Fix this to use some mechanism other than voxels - //Voxels.setVoxel(controlVoxelPosition.x + id * controlVoxelSize, controlVoxelPosition.y, controlVoxelPosition.z, - // controlVoxelSize, COLORS[action].red, COLORS[action].green, COLORS[action].blue); + + var position = { x: controlEntityPosition.x + id * controlEntitySize, + y: controlEntityPosition.y, z: controlEntityPosition.z }; + Entities.addEntity({ + type: "Box", + position: position, + dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, + color: COLORS[action], + lifetime: 5 + }); } function mousePressEvent(event) { diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index 78fe3903cd..93c71aa1a1 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -12,27 +12,25 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; // Set the following variables to the values needed -var filename = HIFI_PUBLIC_BUCKET + "ozan/bartender.rec"; +var filename = "/Users/clement/Desktop/recording.hfr"; var playFromCurrentLocation = true; var useDisplayName = true; var useAttachments = true; -var useHeadModel = true; -var useSkeletonModel = true; +var useAvatarModel = true; // ID of the agent. Two agents can't have the same ID. var id = 0; -// Set head and skeleton models -Avatar.faceModelURL = "http://public.highfidelity.io/models/heads/EvilPhilip_v7.fst"; -Avatar.skeletonModelURL = "http://public.highfidelity.io/models/skeletons/Philip_Carl_Body_A-Pose.fst"; +// Set avatar model URL +Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst?1427169998"; // Set position/orientation/scale here if playFromCurrentLocation is true Avatar.position = { x:1, y: 1, z: 1 }; Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); Avatar.scale = 1.0; // Those variables MUST be common to every scripts -var controlVoxelSize = 0.25; -var controlVoxelPosition = { x: 2000 , y: 0, z: 0 }; +var controlEntitySize = 0.25; +var controlEntityPosition = { x: 2000, y: 0, z: 0 }; // Script. DO NOT MODIFY BEYOND THIS LINE. var DO_NOTHING = 0; @@ -49,113 +47,111 @@ COLORS[STOP] = { red: STOP, green: 0, blue: 0 }; COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 }; COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 }; -controlVoxelPosition.x += id * controlVoxelSize; - +controlEntityPosition.x += id * controlEntitySize; + Avatar.loadRecording(filename); Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); Avatar.setPlayerUseDisplayName(useDisplayName); Avatar.setPlayerUseAttachments(useAttachments); -Avatar.setPlayerUseHeadModel(useHeadModel); -Avatar.setPlayerUseSkeletonModel(useSkeletonModel); +Avatar.setPlayerUseHeadModel(false); +Avatar.setPlayerUseSkeletonModel(useAvatarModel); -function setupVoxelViewer() { - var voxelViewerOffset = 10; - var voxelViewerPosition = JSON.parse(JSON.stringify(controlVoxelPosition)); - voxelViewerPosition.x -= voxelViewerOffset; - var voxelViewerOrientation = Quat.fromPitchYawRollDegrees(0, -90, 0); - - VoxelViewer.setPosition(voxelViewerPosition); - VoxelViewer.setOrientation(voxelViewerOrientation); - VoxelViewer.queryOctree(); +function setupEntityViewer() { + var entityViewerOffset = 10; + var entityViewerPosition = { x: controlEntityPosition.x - entityViewerOffset, + y: controlEntityPosition.y, z: controlEntityPosition.z }; + var entityViewerOrientation = Quat.fromPitchYawRollDegrees(0, -90, 0); + + EntityViewer.setPosition(entityViewerPosition); + EntityViewer.setOrientation(entityViewerOrientation); + EntityViewer.queryOctree(); } -function getAction(controlVoxel) { - if (controlVoxel.x != controlVoxelPosition.x || - controlVoxel.y != controlVoxelPosition.y || - controlVoxel.z != controlVoxelPosition.z || - controlVoxel.s != controlVoxelSize) { - return DO_NOTHING; - } - - for (i in COLORS) { - if (controlVoxel.red === COLORS[i].red && - controlVoxel.green === COLORS[i].green && - controlVoxel.blue === COLORS[i].blue) { - - // TODO: Fix this to use some mechanism other than voxels - //Voxels.eraseVoxel(controlVoxelPosition.x, controlVoxelPosition.y, controlVoxelPosition.z, controlVoxelSize); - return parseInt(i); +function getAction(controlEntity) { + if (controlEntity === null || + controlEntity.position.x !== controlEntityPosition.x || + controlEntity.position.y !== controlEntityPosition.y || + controlEntity.position.z !== controlEntityPosition.z || + controlEntity.dimensions.x !== controlEntitySize) { + return DO_NOTHING; } - } - - return DO_NOTHING; + + for (i in COLORS) { + if (controlEntity.color.red === COLORS[i].red && + controlEntity.color.green === COLORS[i].green && + controlEntity.color.blue === COLORS[i].blue) { + Entities.deleteEntity(controlEntity.id); + return parseInt(i); + } + } + + return DO_NOTHING; } -count = 300; // This is necessary to wait for the audio mixer to connect +count = 100; // This is necessary to wait for the audio mixer to connect function update(event) { - VoxelViewer.queryOctree(); - if (count > 0) { - count--; - return; - } - - // TODO: Fix this to use some mechanism other than voxels - // Voxels.getVoxelAt(controlVoxelPosition.x, controlVoxelPosition.y, controlVoxelPosition.z, controlVoxelSize); - var controlVoxel = false; - var action = getAction(controlVoxel); - - switch(action) { - case PLAY: - print("Play"); - if (!Agent.isAvatar) { - Agent.isAvatar = true; - } - if (!Avatar.isPlaying()) { - Avatar.startPlaying(); - } - Avatar.setPlayerLoop(false); - break; - case PLAY_LOOP: - print("Play loop"); - if (!Agent.isAvatar) { - Agent.isAvatar = true; - } - if (!Avatar.isPlaying()) { - Avatar.startPlaying(); - } - Avatar.setPlayerLoop(true); - break; - case STOP: - print("Stop"); - if (Avatar.isPlaying()) { - Avatar.stopPlaying(); - } - break; - case SHOW: - print("Show"); - if (!Agent.isAvatar) { - Agent.isAvatar = true; - } - break; - case HIDE: - print("Hide"); - if (Avatar.isPlaying()) { - Avatar.stopPlaying(); - } - Agent.isAvatar = false; - break; - case DO_NOTHING: - break; - default: - print("Unknown action: " + action); - break; - } - - if (Avatar.isPlaying()) { - Avatar.play(); - } + EntityViewer.queryOctree(); + if (count > 0) { + count--; + return; + } + + + var controlEntity = Entities.findClosestEntity(controlEntityPosition, controlEntitySize); + var action = getAction(Entities.getEntityProperties(controlEntity)); + + switch(action) { + case PLAY: + print("Play"); + if (!Agent.isAvatar) { + Agent.isAvatar = true; + } + if (!Avatar.isPlaying()) { + Avatar.startPlaying(); + } + Avatar.setPlayerLoop(false); + break; + case PLAY_LOOP: + print("Play loop"); + if (!Agent.isAvatar) { + Agent.isAvatar = true; + } + if (!Avatar.isPlaying()) { + Avatar.startPlaying(); + } + Avatar.setPlayerLoop(true); + break; + case STOP: + print("Stop"); + if (Avatar.isPlaying()) { + Avatar.stopPlaying(); + } + break; + case SHOW: + print("Show"); + if (!Agent.isAvatar) { + Agent.isAvatar = true; + } + break; + case HIDE: + print("Hide"); + if (Avatar.isPlaying()) { + Avatar.stopPlaying(); + } + Agent.isAvatar = false; + break; + case DO_NOTHING: + break; + default: + print("Unknown action: " + action); + break; + } + + if (Avatar.isPlaying()) { + Avatar.play(); + } } Script.update.connect(update); -setupVoxelViewer(); \ No newline at end of file +setupEntityViewer(); diff --git a/examples/acScripts/PlayRecordingOnAC.js b/examples/acScripts/PlayRecordingOnAC.js index 76f00ab9cd..b7ae2c7329 100644 --- a/examples/acScripts/PlayRecordingOnAC.js +++ b/examples/acScripts/PlayRecordingOnAC.js @@ -14,8 +14,7 @@ var filename = "http://your.recording.url"; var playFromCurrentLocation = true; var loop = true; -Avatar.faceModelURL = "http://public.highfidelity.io/models/heads/EvilPhilip_v7.fst"; -Avatar.skeletonModelURL = "http://public.highfidelity.io/models/skeletons/Philip_Carl_Body_A-Pose.fst"; +Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst?1427169998"; // Set position here if playFromCurrentLocation is true Avatar.position = { x:1, y: 1, z: 1 }; @@ -23,30 +22,34 @@ Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); Avatar.scale = 1.0; Agent.isAvatar = true; - + Avatar.loadRecording(filename); count = 300; // This is necessary to wait for the audio mixer to connect function update(event) { - if (count > 0) { - count--; - return; - } - if (count == 0) { - Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); - Avatar.setPlayerLoop(loop); - Avatar.startPlaying(); - Avatar.play(); - Vec3.print("Playing from ", Avatar.position); - - count--; - } - - if (Avatar.isPlaying()) { - Avatar.play(); - } else { - Script.update.disconnect(update); - } + if (count > 0) { + count--; + return; + } + if (count == 0) { + Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); + Avatar.setPlayerLoop(loop); + Avatar.setPlayerUseDisplayName(true); + Avatar.setPlayerUseAttachments(true); + Avatar.setPlayerUseHeadModel(false); + Avatar.setPlayerUseSkeletonModel(true); + Avatar.startPlaying(); + Avatar.play(); + Vec3.print("Playing from ", Avatar.position); + + count--; + } + + if (Avatar.isPlaying()) { + Avatar.play(); + } else { + Script.update.disconnect(update); + } } Script.update.connect(update); diff --git a/examples/utilities/record/recorder.js b/examples/utilities/record/recorder.js index 495a862db1..6d49030a28 100644 --- a/examples/utilities/record/recorder.js +++ b/examples/utilities/record/recorder.js @@ -112,10 +112,9 @@ function setupTimer() { text: (0.00).toFixed(3), backgroundColor: COLOR_OFF, x: 0, y: 0, - width: 0, - height: 0, - alpha: 1.0, - backgroundAlpha: 1.0, + width: 0, height: 0, + leftMargin: 10, topMargin: 10, + alpha: 1.0, backgroundAlpha: 1.0, visible: true }); diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index a425323a41..5e29963e4b 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -397,16 +397,15 @@ bool Player::computeCurrentFrame() { } qint64 elapsed = glm::clamp(Player::elapsed() - _audioOffset, (qint64)0, (qint64)_recording->getLength()); - while(_currentFrame >= 0 && - _recording->getFrameTimestamp(_currentFrame) > elapsed) { - --_currentFrame; - } - while (_currentFrame < _recording->getFrameNumber() && _recording->getFrameTimestamp(_currentFrame) < elapsed) { ++_currentFrame; } - --_currentFrame; + + while(_currentFrame > 0 && + _recording->getFrameTimestamp(_currentFrame) > elapsed) { + --_currentFrame; + } if (_currentFrame == _recording->getFrameNumber() - 1) { --_currentFrame; diff --git a/libraries/avatars/src/Recorder.h b/libraries/avatars/src/Recorder.h index 2723ebebdb..f81539a417 100644 --- a/libraries/avatars/src/Recorder.h +++ b/libraries/avatars/src/Recorder.h @@ -43,7 +43,6 @@ public slots: void record(); void recordAudio(const QByteArray& audioArray); - private: QElapsedTimer _timer; RecordingPointer _recording; From f8de01a856c3e5e85da6e2e2a322b32669eec911 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 5 Nov 2015 14:14:49 -0800 Subject: [PATCH 0723/1003] Fixed bug where eraser wouldn't erase --- .../painting/whiteboard/whiteboardSpawner.js | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index 53bb7158d5..56ead143e4 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -87,12 +87,24 @@ var light = Entities.addEntity({ type: 'Light', name: 'whiteboard light', position: lightPosition, - dimensions: {x: 10, y: 10, z: 10}, + dimensions: { + x: 10, + y: 10, + z: 10 + }, intensity: 2, - color: {red: 255, green: 255, blue: 255} + color: { + red: 255, + green: 255, + blue: 255 + } }); -var eraserPosition = Vec3.sum(center, {x: 0, y: 2.05, z: 0 }); +var eraserPosition = Vec3.sum(center, { + x: 0, + y: 2.05, + z: 0 +}); eraserPosition = Vec3.sum(eraserPosition, Vec3.multiply(-0.1, rotation)); scriptURL = Script.resolvePath("eraseBoardEntityScript.js"); var eraser = Entities.addEntity({ @@ -103,7 +115,10 @@ var eraser = Entities.addEntity({ script: scriptURL, rotation: rotation, userData: JSON.stringify({ - whiteboard: drawingSurface + whiteboard: drawingSurface, + grabbableKey: { + wantsTrigger: true + } }) }); @@ -115,20 +130,32 @@ Script.setTimeout(function() { function setUp() { - var blockerPosition = Vec3.sum(center, {x: 0, y: -1, z: 0 }); + var blockerPosition = Vec3.sum(center, { + x: 0, + y: -1, + z: 0 + }); blockerPosition = Vec3.sum(blockerPosition, Vec3.multiply(-1, Quat.getFront(rotation))); blocker = Entities.addEntity({ type: "Box", rotation: rotation, position: blockerPosition, - dimensions: {x: whiteboardDimensions.x, y: 1, z: 0.1}, + dimensions: { + x: whiteboardDimensions.x, + y: 1, + z: 0.1 + }, shapeType: "box", visible: false }); var eraseModelDimensions = Entities.getEntityProperties(eraser, "naturalDimensions").naturalDimensions; - Entities.editEntity(eraser, {dimensions: eraseModelDimensions}); - Entities.editEntity(colorIndicatorBorder, {dimensions: colorIndicatorBorderDimensions}); + Entities.editEntity(eraser, { + dimensions: eraseModelDimensions + }); + Entities.editEntity(colorIndicatorBorder, { + dimensions: colorIndicatorBorderDimensions + }); scriptURL = Script.resolvePath("colorIndicatorEntityScript.js"); var colorIndicatorPosition = Vec3.sum(center, { @@ -193,7 +220,9 @@ function setUp() { userData: JSON.stringify({ whiteboard: drawingSurface, colorIndicator: colorIndicatorBox, - grabbableKey: {wantsTrigger: true} + grabbableKey: { + wantsTrigger: true + } }) }); colorBoxes.push(colorBox); @@ -218,4 +247,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -// Script.scriptEnding.connect(cleanup); +// Script.scriptEnding.connect(cleanup); \ No newline at end of file From 751d51ee722e836395cee0e9a69024282b4eaee6 Mon Sep 17 00:00:00 2001 From: James Pollack Date: Thu, 5 Nov 2015 14:19:11 -0800 Subject: [PATCH 0724/1003] entity call params --- .../entities/src/EntityScriptingInterface.cpp | 4 +-- libraries/script-engine/src/ScriptEngine.cpp | 31 +++++++++++++++++++ libraries/script-engine/src/ScriptEngine.h | 1 + 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 01d46e0a91..719b193dfd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -217,10 +217,10 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } } -void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method) { +void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { if (_entitiesScriptEngine) { EntityItemID entityID{ id }; - _entitiesScriptEngine->callEntityScriptMethod(entityID, method); + _entitiesScriptEngine->callEntityScriptMethod(entityID, method, params); } } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c6ac6495b1..e9b2bc9acd 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1200,6 +1200,37 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS } } +void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) { + if (QThread::currentThread() != thread()) { + #ifdef THREAD_DEBUGGING + qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " + "entityID:" << entityID << "methodName:" << methodName; + #endif + + QMetaObject::invokeMethod(this, "callEntityScriptMethod", + Q_ARG(const EntityItemID&, entityID), + Q_ARG(const QString&, methodName), + Q_ARG(const QStringList&, params)); + return; + } + #ifdef THREAD_DEBUGGING + qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " + "entityID:" << entityID << "methodName:" << methodName; + #endif + + refreshFileScript(entityID); + if (_entityScripts.contains(entityID)) { + EntityScriptDetails details = _entityScripts[entityID]; + QScriptValue entityScript = details.scriptObject; // previously loaded + if (entityScript.property(methodName).isFunction()) { + QScriptValueList args; + args << entityID.toScriptValue(this); + entityScript.property(methodName).call(entityScript, args); + } + + } +} + void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 43fbdc1b0e..7ce79b4bb0 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -116,6 +116,7 @@ public: Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName); + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); From b229b5289cc7907c120b042fa0bc44085bb19a2c Mon Sep 17 00:00:00 2001 From: James Pollack Date: Thu, 5 Nov 2015 14:24:21 -0800 Subject: [PATCH 0725/1003] entity call params --- libraries/entities/src/EntityScriptingInterface.cpp | 8 +++++++- libraries/entities/src/EntityScriptingInterface.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 719b193dfd..a68f946173 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -217,6 +217,13 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } } +void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method) { + if (_entitiesScriptEngine) { + EntityItemID entityID{ id }; + _entitiesScriptEngine->callEntityScriptMethod(entityID, method); + } +} + void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { if (_entitiesScriptEngine) { EntityItemID entityID{ id }; @@ -224,7 +231,6 @@ void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, } } - QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { EntityItemID result; if (_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 156f16cf46..05c48a8415 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -94,7 +94,7 @@ public slots: /// engine. If the entity does not have an entity script or the method does not exist, this call will have /// no effect. Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method); - + Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params); /// finds the closest model to the center point, within the radius /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models From 28d4d29bb60bd4247f829fdf3aef5d3820485ec6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 5 Nov 2015 15:07:00 -0800 Subject: [PATCH 0726/1003] smooth hold action velocity over 2 frames --- interface/src/avatar/AvatarActionHold.cpp | 6 +++++- interface/src/avatar/AvatarActionHold.h | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 70216eca11..6078d326e0 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -107,8 +107,12 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { withWriteLock([&]{ if (_kinematicSetVelocity) { if (_previousSet) { - glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + // smooth velocity over 2 frames + glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget; + glm::vec3 positionalVelocity = (positionalDelta + previousPositionalDelta) / (deltaTimeStep + previousDeltaTimeStep); rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); + previousPositionalDelta = positionalDelta; + previousDeltaTimeStep = deltaTimeStep; } } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 6badf97e9e..4856a17b89 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -46,6 +46,9 @@ private: bool _previousSet { false }; glm::vec3 _previousPositionalTarget; glm::quat _previousRotationalTarget; + + float previousDeltaTimeStep = 0.0f; + glm::vec3 previousPositionalDelta; }; #endif // hifi_AvatarActionHold_h From aa1289bd7c0760bbc8219b599ba2a59b34edcb61 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 5 Nov 2015 15:53:46 -0800 Subject: [PATCH 0727/1003] Update agent's outgoing avatar data packet with sequenceNumber as is now used in interface's MyAvatar. --- assignment-client/src/Agent.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index d9109703cb..063bf24de8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -267,7 +267,10 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO); _avatarData->doneEncoding(true); - auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); + + static AvatarDataSequenceNumber sequenceNumber = 0; + auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size() + sizeof(sequenceNumber)); + avatarPacket->writePrimitive(sequenceNumber++); avatarPacket->write(avatarByteArray); From ffafd3194e7eb5451ac34e3b65dc03fcce029a1b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Nov 2015 15:02:21 -0800 Subject: [PATCH 0728/1003] Recording audio fixes --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/audio-client/src/AudioClient.cpp | 11 ++++------- libraries/audio/src/AudioInjector.cpp | 4 ++-- libraries/avatars/src/Recording.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5bd3e82ffd..57af9e732d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -604,7 +604,7 @@ void MyAvatar::startRecording() { // connect to AudioClient's signal so we get input audio auto audioClient = DependencyManager::get(); connect(audioClient.data(), &AudioClient::inputReceived, _recorder.data(), - &Recorder::recordAudio, Qt::BlockingQueuedConnection); + &Recorder::recordAudio, Qt::QueuedConnection); _recorder->startRecording(); } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d4980596dd..936411d786 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -802,16 +802,14 @@ void AudioClient::handleAudioInput() { _timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE; } - int16_t* inputAudioSamples = new int16_t[inputSamplesRequired]; - _inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired); + auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); + _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); possibleResampling(_inputToNetworkResampler, - inputAudioSamples, networkAudioSamples, + inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired, numNetworkSamples, _inputFormat, _desiredInputFormat); - delete[] inputAudioSamples; - // Remove DC offset if (!_isStereoInput && !_audioSourceInjectEnabled) { _inputGate.removeDCOffset(networkAudioSamples, numNetworkSamples); @@ -842,8 +840,7 @@ void AudioClient::handleAudioInput() { _lastInputLoudness = fabs(loudness / numNetworkSamples); } - emit inputReceived(QByteArray(reinterpret_cast(networkAudioSamples), - AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * sizeof(AudioConstants::AudioSample))); + emit inputReceived({reinterpret_cast(networkAudioSamples), numNetworkBytes}); } else { // our input loudness is 0, since we're muted diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 334c44c4c5..bd807f8dbd 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -205,7 +205,7 @@ void AudioInjector::injectToMixer() { while (_currentSendOffset < _audioData.size() && !_shouldStop) { - int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, + int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, _audioData.size() - _currentSendOffset); // Measure the loudness of this frame @@ -261,7 +261,7 @@ void AudioInjector::injectToMixer() { // not the first packet and not done // sleep for the appropriate time - int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; + int usecToSleep = (++nextFrame * (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; if (usecToSleep > 0) { usleep(usecToSleep); diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 5514b97b6f..2e2f46552d 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -69,10 +69,10 @@ const RecordingFrame& Recording::getFrame(int i) const { int Recording::numberAudioChannel() const { // Check for stereo audio - int MSEC_PER_SEC = 1000; - int channelLength = (getLength() / MSEC_PER_SEC) * - AudioConstants::SAMPLE_RATE * sizeof(AudioConstants::AudioSample); - return glm::round((float)channelLength / (float)getAudioData().size()); + float MSEC_PER_SEC = 1000.0f; + float channelLength = ((float)getLength() / MSEC_PER_SEC) * AudioConstants::SAMPLE_RATE * + sizeof(AudioConstants::AudioSample); + return glm::round((float)getAudioData().size() / channelLength); } void Recording::addFrame(int timestamp, RecordingFrame &frame) { From 6c35ae48b09bd270598ac9d1713c398f74d75705 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Nov 2015 16:52:38 -0800 Subject: [PATCH 0729/1003] Don't allocate big samples array in tight loop --- libraries/audio-client/src/AudioClient.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 936411d786..2883bfe332 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -749,8 +749,9 @@ void AudioClient::handleAudioInput() { } float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(); - + int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); + auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); static int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); int16_t* networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); @@ -802,9 +803,7 @@ void AudioClient::handleAudioInput() { _timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE; } - auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); - possibleResampling(_inputToNetworkResampler, inputAudioSamples.get(), networkAudioSamples, inputSamplesRequired, numNetworkSamples, From 3cee569baefd46d9ed8c44fc005f0e558f40a63b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 5 Nov 2015 16:57:39 -0800 Subject: [PATCH 0730/1003] pass parameters to entities --- libraries/entities/src/EntitiesScriptEngineProvider.h | 1 + libraries/entities/src/EntityScriptingInterface.h | 1 + libraries/script-engine/src/ScriptEngine.cpp | 2 ++ libraries/script-engine/src/ScriptEngine.h | 1 + 4 files changed, 5 insertions(+) diff --git a/libraries/entities/src/EntitiesScriptEngineProvider.h b/libraries/entities/src/EntitiesScriptEngineProvider.h index d112a6c0f9..1922f660cd 100644 --- a/libraries/entities/src/EntitiesScriptEngineProvider.h +++ b/libraries/entities/src/EntitiesScriptEngineProvider.h @@ -20,6 +20,7 @@ class EntitiesScriptEngineProvider { public: virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) = 0; + virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) = 0; }; #endif // hifi_EntitiesScriptEngineProvider_h \ No newline at end of file diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 05c48a8415..afec8e3680 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -15,6 +15,7 @@ #define hifi_EntityScriptingInterface_h #include +#include #include #include diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index e9b2bc9acd..f64efa8c1c 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1225,6 +1226,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS if (entityScript.property(methodName).isFunction()) { QScriptValueList args; args << entityID.toScriptValue(this); + args << qScriptValueFromSequence(this, params); entityScript.property(methodName).call(entityScript, args); } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 7ce79b4bb0..447a22fe5e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include From dbe25651cf3b8a67ad300e8a52799e8ee82c6153 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Nov 2015 16:57:51 -0800 Subject: [PATCH 0731/1003] constify audio client loop --- libraries/audio-client/src/AudioClient.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2883bfe332..72e47073f2 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -748,13 +748,13 @@ void AudioClient::handleAudioInput() { _audioPacket = NLPacket::create(PacketType::MicrophoneAudioNoEcho); } - float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(); + const float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(); - int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); - auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); + const int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); + const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); - static int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); - int16_t* networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); + static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); + int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); QByteArray inputByteArray = _inputDevice->readAll(); From de98a17ada0c21f57bfffe92a45bd67326f38399 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 6 Nov 2015 01:58:08 +0100 Subject: [PATCH 0732/1003] - rename CameraEntityItem to AnchorEntityItem --- examples/edit.js | 14 +++++------ examples/example/securityCamera.js | 6 ++--- examples/libraries/entityPropertyDialogBox.js | 2 +- interface/src/Application.cpp | 6 ++--- interface/src/Camera.cpp | 23 ++++++------------ interface/src/Camera.h | 4 ++-- interface/src/Menu.h | 2 +- ...eraEntityItem.cpp => AnchorEntityItem.cpp} | 24 +++++++++---------- ...{CameraEntityItem.h => AnchorEntityItem.h} | 12 +++++----- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityTypes.cpp | 4 ++-- libraries/entities/src/EntityTypes.h | 4 ++-- 12 files changed, 47 insertions(+), 56 deletions(-) rename libraries/entities/src/{CameraEntityItem.cpp => AnchorEntityItem.cpp} (76%) rename libraries/entities/src/{CameraEntityItem.h => AnchorEntityItem.h} (87%) diff --git a/examples/edit.js b/examples/edit.js index ff4021c46f..cf5163b824 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -155,7 +155,7 @@ var toolBar = (function() { newWebButton, newZoneButton, newPolyVoxButton, - newCameraButton, + newAnchorButton, browseMarketplaceButton; function initialize() { @@ -300,8 +300,8 @@ var toolBar = (function() { visible: false }); - newCameraButton = toolBar.addTool({ - imageURL: toolIconUrl + "light.svg", + newAnchorButton = toolBar.addTool({ + imageURL: toolIconUrl + "add-anchor.svg", subImage: { x: 0, y: Tool.IMAGE_WIDTH, @@ -360,7 +360,7 @@ var toolBar = (function() { toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newPolyVoxButton, doShow); - toolBar.showTool(newCameraButton, doShow); + toolBar.showTool(newAnchorButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -619,9 +619,9 @@ var toolBar = (function() { return true; } - if (newCameraButton === toolBar.clicked(clickedOverlay)) { + if (newAnchorButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ - type: "Camera" + type: "Anchor" }); return true; @@ -1642,7 +1642,7 @@ PropertiesTool = function(opts) { } } else if (data.action == "previewCamera") { if (selectionManager.hasSelection()) { - Camera.mode = "camera entity"; + Camera.mode = "entity"; Camera.cameraEntity = selectionManager.selections[0]; } } else if (data.action == "rescaleDimensions") { diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js index 19c381d3df..15d2c03e2e 100644 --- a/examples/example/securityCamera.js +++ b/examples/example/securityCamera.js @@ -28,11 +28,11 @@ var cameraLookAt = function(cameraPos, lookAtPos) { }; cameraEntity = Entities.addEntity({ - type: "Camera", - position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) + type: "Anchor", + position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) }); -Camera.mode = "camera entity"; +Camera.mode = "entity"; Camera.cameraEntity = cameraEntity; Script.update.connect(function(deltaTime) { diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index b47e26579d..b34bec2a25 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -275,7 +275,7 @@ EntityPropertyDialogBox = (function () { Window.inlineButtonClicked.connect(function (name) { if (name == "previewCamera") { - Camera.mode = "camera entity"; + Camera.mode = "entity"; Camera.cameraEntity = propertiesForEditedEntity.id; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 35aeba8602..7d82f2269b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1200,7 +1200,7 @@ void Application::paintGL() { glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror); } renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; - } else if (_myCamera.getMode() == CAMERA_MODE_CAMERA_ENTITY) { + } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); if (cameraEntity != nullptr) { if (isHMDMode()) { @@ -2687,8 +2687,8 @@ void Application::cameraMenuChanged() { _myCamera.setMode(CAMERA_MODE_INDEPENDENT); } } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) { - if (_myCamera.getMode() != CAMERA_MODE_CAMERA_ENTITY) { - _myCamera.setMode(CAMERA_MODE_CAMERA_ENTITY); + if (_myCamera.getMode() != CAMERA_MODE_ENTITY) { + _myCamera.setMode(CAMERA_MODE_ENTITY); } } } diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index ae654c4906..53a3500bff 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -28,8 +28,8 @@ CameraMode stringToMode(const QString& mode) { return CAMERA_MODE_MIRROR; } else if (mode == "independent") { return CAMERA_MODE_INDEPENDENT; - } else if (mode == "camera entity") { - return CAMERA_MODE_CAMERA_ENTITY; + } else if (mode == "entity") { + return CAMERA_MODE_ENTITY; } return CAMERA_MODE_NULL; } @@ -43,8 +43,8 @@ QString modeToString(CameraMode mode) { return "mirror"; } else if (mode == CAMERA_MODE_INDEPENDENT) { return "independent"; - } else if (mode == CAMERA_MODE_CAMERA_ENTITY) { - return "camera entity"; + } else if (mode == CAMERA_MODE_ENTITY) { + return "entity"; } return "unknown"; } @@ -105,17 +105,8 @@ QUuid Camera::getCameraEntity() const { return QUuid(); }; -void Camera::setCameraEntity(QUuid cameraEntityID) { - EntityItemPointer entity = qApp->getEntities()->getTree()->findEntityByID(cameraEntityID); - if (entity == nullptr) { - qDebug() << "entity pointer not found"; - return; - } - if (entity->getType() != EntityTypes::Camera) { - qDebug() << "entity type is not camera"; - return; - } - _cameraEntity = entity; +void Camera::setCameraEntity(QUuid entityID) { + _cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID); } void Camera::setProjection(const glm::mat4& projection) { @@ -142,7 +133,7 @@ void Camera::setModeString(const QString& mode) { case CAMERA_MODE_INDEPENDENT: Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); break; - case CAMERA_MODE_CAMERA_ENTITY: + case CAMERA_MODE_ENTITY: Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); break; default: diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 7aa31456e9..017bd742a4 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -24,7 +24,7 @@ enum CameraMode CAMERA_MODE_FIRST_PERSON, CAMERA_MODE_MIRROR, CAMERA_MODE_INDEPENDENT, - CAMERA_MODE_CAMERA_ENTITY, + CAMERA_MODE_ENTITY, NUM_CAMERA_MODES }; @@ -73,7 +73,7 @@ public slots: void setProjection(const glm::mat4& projection); QUuid getCameraEntity() const; - void setCameraEntity(QUuid cameraEntityID); + void setCameraEntity(QUuid entityID); PickRay computePickRay(float x, float y); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 5c84bb11fa..5964049f93 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -157,7 +157,7 @@ namespace MenuOption { const QString Bookmarks = "Bookmarks"; const QString CachesSize = "RAM Caches Size"; const QString CalibrateCamera = "Calibrate Camera"; - const QString CameraEntityMode = "Camera Entity"; + const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString Collisions = "Collisions"; diff --git a/libraries/entities/src/CameraEntityItem.cpp b/libraries/entities/src/AnchorEntityItem.cpp similarity index 76% rename from libraries/entities/src/CameraEntityItem.cpp rename to libraries/entities/src/AnchorEntityItem.cpp index 8370447389..d84ac3c3a5 100644 --- a/libraries/entities/src/CameraEntityItem.cpp +++ b/libraries/entities/src/AnchorEntityItem.cpp @@ -1,5 +1,5 @@ // -// CameraEntityItem.cpp +// AnchorEntityItem.cpp // libraries/entities/src // // Created by Thijs Wenker on 11/4/15. @@ -12,30 +12,30 @@ #include "EntitiesLogging.h" #include "EntityItemID.h" #include "EntityItemProperties.h" -#include "CameraEntityItem.h" +#include "AnchorEntityItem.h" -EntityItemPointer CameraEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer result { new CameraEntityItem(entityID, properties) }; +EntityItemPointer AnchorEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer result { new AnchorEntityItem(entityID, properties) }; return result; } // our non-pure virtual subclass for now... -CameraEntityItem::CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : +AnchorEntityItem::AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : EntityItem(entityItemID) { - _type = EntityTypes::Camera; + _type = EntityTypes::Anchor; setProperties(properties); } -EntityItemProperties CameraEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { +EntityItemProperties AnchorEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class return properties; } -bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { +bool AnchorEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -44,7 +44,7 @@ bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { if (wantDebug) { uint64_t now = usecTimestampNow(); int elapsed = now - getLastEdited(); - qCDebug(entities) << "CameraEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + qCDebug(entities) << "AnchorEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << "now=" << now << " getLastEdited()=" << getLastEdited(); } setLastEdited(properties._lastEdited); @@ -53,7 +53,7 @@ bool CameraEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } -int CameraEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, +int AnchorEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { @@ -66,12 +66,12 @@ int CameraEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data // TODO: eventually only include properties changed since the params.lastViewFrustumSent time -EntityPropertyFlags CameraEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { +EntityPropertyFlags AnchorEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); return requestedProperties; } -void CameraEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, +void AnchorEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, diff --git a/libraries/entities/src/CameraEntityItem.h b/libraries/entities/src/AnchorEntityItem.h similarity index 87% rename from libraries/entities/src/CameraEntityItem.h rename to libraries/entities/src/AnchorEntityItem.h index 6256c200c2..bd385517c3 100644 --- a/libraries/entities/src/CameraEntityItem.h +++ b/libraries/entities/src/AnchorEntityItem.h @@ -1,5 +1,5 @@ // -// CameraEntityItem.h +// AnchorEntityItem.h // libraries/entities/src // // Created by Thijs Wenker on 11/4/15. @@ -9,16 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_CameraEntityItem_h -#define hifi_CameraEntityItem_h +#ifndef hifi_AnchorEntityItem_h +#define hifi_AnchorEntityItem_h #include "EntityItem.h" -class CameraEntityItem : public EntityItem { +class AnchorEntityItem : public EntityItem { public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - CameraEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); + AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); ALLOW_INSTANTIATION // This class can be instantiated @@ -44,4 +44,4 @@ public: }; -#endif // hifi_CameraEntityItem_h +#endif // hifi_AnchorEntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4522e4fc45..60199d9784 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,7 +64,7 @@ class EntityItemProperties { friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class CameraEntityItem; // TODO: consider removing this friend relationship and use public methods + friend class AnchorEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index b7d9077f57..f5915ae8c3 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,7 +29,7 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -#include "CameraEntityItem.h" +#include "AnchorEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -50,7 +50,7 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) -REGISTER_ENTITY_TYPE(Camera); +REGISTER_ENTITY_TYPE(Anchor); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 82bbffe254..dbe9569f63 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,8 +48,8 @@ public: Line, PolyVox, PolyLine, - Camera, - LAST = Camera + Anchor, + LAST = Anchor } EntityType; static const QString& getEntityTypeName(EntityType entityType); From 94c5637024bb3d5f2aa2f24b8f62f0e17d27f996 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Thu, 5 Nov 2015 17:27:30 -0800 Subject: [PATCH 0733/1003] marketplace spawner --- examples/marketplace/marketplaceSpawner.js | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/marketplace/marketplaceSpawner.js diff --git a/examples/marketplace/marketplaceSpawner.js b/examples/marketplace/marketplaceSpawner.js new file mode 100644 index 0000000000..3e655e3e74 --- /dev/null +++ b/examples/marketplace/marketplaceSpawner.js @@ -0,0 +1,50 @@ +var floorPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));; +floorPosition.y = MyAvatar.position.y - 5; + +var modelsToLoad = [ + { + lowURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_low.fbx", + highURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_hi.fbx" + } +]; + +var models = []; + +var floor = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/ozan/3d_marketplace/props/floor/3d_mp_floor.fbx", + position: floorPosition, + shapeType: 'box', + dimensions: {x: 1000, y: 9, z: 1000} +}); + +//Create grid +var modelParams = { + type: "Model", + shapeType: "box", + dimensions: {x: 53, y: 15.7, z: 44.8}, + velocity: {x: 0, y: -1, z: 0}, + gravity: {x: 0, y: -1, z: 0}, + collisionsWillMove: true +}; + +var modelPosition = {x: floorPosition.x + 10, y: floorPosition.y + 15, z: floorPosition.z}; +for (var i = 0; i < modelsToLoad.length; i++) { + modelParams.modelURL = modelsToLoad[i].lowURL; + modelPosition.z -= 10; + modelParams.position = modelPosition; + var model = Entities.addEntity(modelParams); + models.push(model); +} + + + + +function cleanup() { + Entities.deleteEntity(floor); + models.forEach(function(model) { + Entities.deleteEntity(model); + }); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file From c6b42f5fbc4cbcdd3a75969ddd7a4243493c0a1b Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 5 Nov 2015 18:48:24 -0800 Subject: [PATCH 0734/1003] Added a second way to create mapping --- examples/controllers/controllerMappings.js | 68 +++++++++++++--------- examples/controllers/rightClickExample.js | 10 ---- 2 files changed, 39 insertions(+), 39 deletions(-) delete mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index a44833f80a..1bcd05a4cc 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -4,68 +4,74 @@ // examples // // Created by Sam Gondelman on 6/2/15 +// Rewritten by Alessandro Signa on 11/05/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 // -// This allows to change the input mapping making a sense of how the new mapping works +// This allows to change the input mapping to easly understand of how the new mapping works. +// Two different ways are presented: the first one uses a JSON file to create the mapping, the second one declares the new routes explicitly one at a time. +// You shuold prefer the first method if you have a lot of new routes, the second one if you want to express the action as a function. /* -This function returns a JSON body. It's in charge to modify the mouse/keyboard mapping. The keyboard inputs are mapped directly to actions. +This function returns a JSON body. It's in charge to modify the standard controller and the mouse/keyboard mapping. + +The Standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) +This example will overwrite the mapping of the left axis (Standard.LY, Standard.LX). +It's possible to find all the standard inputs (and their mapping) into standard.json +To try these changes you need a controller, not the keyboard. + +The keyboard/mouse inputs are mapped directly to actions since the keyboard doesn't have its default mapping passing through the Standard controller. If this new mapping contains inputs which are defined in the standard mapping, these will overwrite the old ones(Keyboard.W, Keyboard.RightMouseButton). If this new mapping contains inputs which are not defined in the standard, these will be added to the mapping(Keyboard.M). */ myFirstMapping = function() { return { - "name": "example", + "name": "controllerMapping_First", "channels": [ - { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.M", "to": "Actions.YAW_LEFT" }, - - { "from": "Keyboard.RightMouseButton", "to": "Actions.YAW_RIGHT" } - ] -} -} -/* -This JSON is in charge to modify the Standard controller mapping. -The standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) -These new inputs will overwrite the standard ones. -It's possible to find all the standard inputs (and their mapping) into standard.json -To try the mySecondMapping effect you need a controller, not the keyboard. -*/ -mySecondMapping = function() { -return { - "name": "example2", - "channels": [ { "from": "Standard.LY", "to": "Actions.Yaw" }, { "from": "Standard.LX", "to": "Actions.Yaw" }, + + { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.M", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.LeftMouseButton", "to": "Actions.Up" } + ] } } -var tryFirst = true; + +var firstWay = true; var mapping; -if(tryFirst){ +var MAPPING_NAME; + + +if(firstWay){ var myFirstMappingJSON = myFirstMapping(); print('myfirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); + mapping.enable(); }else{ - var mySecondMappingJSON = mySecondMapping(); - print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); - mapping = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); + MAPPING_NAME = "controllerMapping_Second"; + var mapping2 = Controller.newMapping(MAPPING_NAME); + mapping2.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); + }); + mapping2.from(Controller.Standard.LX).to(Controller.Actions.Yaw); + Controller.enableMapping(MAPPING_NAME); } -mapping.enable(); /* -//-----------------some info prints----------------------- +//-----------------some info prints that you would like to enable----------------------- Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); @@ -94,5 +100,9 @@ Controller.hardwareChanged.connect(function () { Script.scriptEnding.connect(function () { - mapping.disable(); + if(firstWay){ + mapping.disable(); + } else { + Controller.disableMapping(MAPPING_NAME); + } }); \ No newline at end of file diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js deleted file mode 100644 index c3e6ea8f3d..0000000000 --- a/examples/controllers/rightClickExample.js +++ /dev/null @@ -1,10 +0,0 @@ -var MAPPING_NAME = "com.highfidelity.rightClickExample"; -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { - print("Keyboard.RightMouseClicked"); -}); -Controller.enableMapping(MAPPING_NAME); - -Script.scriptEnding.connect(function () { - Controller.disableMapping(MAPPING_NAME); -}); \ No newline at end of file From 715b183a3c8bbe9f97f8ecf8aecd4c46ef60e446 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 5 Nov 2015 19:10:06 -0800 Subject: [PATCH 0735/1003] unified two ways to do the mapping in a single JS --- examples/controllers/controllerMappings.js | 68 +++++++++++++--------- examples/controllers/rightClickExample.js | 10 ---- 2 files changed, 39 insertions(+), 39 deletions(-) delete mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/controllerMappings.js b/examples/controllers/controllerMappings.js index a44833f80a..1bcd05a4cc 100644 --- a/examples/controllers/controllerMappings.js +++ b/examples/controllers/controllerMappings.js @@ -4,68 +4,74 @@ // examples // // Created by Sam Gondelman on 6/2/15 +// Rewritten by Alessandro Signa on 11/05/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 // -// This allows to change the input mapping making a sense of how the new mapping works +// This allows to change the input mapping to easly understand of how the new mapping works. +// Two different ways are presented: the first one uses a JSON file to create the mapping, the second one declares the new routes explicitly one at a time. +// You shuold prefer the first method if you have a lot of new routes, the second one if you want to express the action as a function. /* -This function returns a JSON body. It's in charge to modify the mouse/keyboard mapping. The keyboard inputs are mapped directly to actions. +This function returns a JSON body. It's in charge to modify the standard controller and the mouse/keyboard mapping. + +The Standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) +This example will overwrite the mapping of the left axis (Standard.LY, Standard.LX). +It's possible to find all the standard inputs (and their mapping) into standard.json +To try these changes you need a controller, not the keyboard. + +The keyboard/mouse inputs are mapped directly to actions since the keyboard doesn't have its default mapping passing through the Standard controller. If this new mapping contains inputs which are defined in the standard mapping, these will overwrite the old ones(Keyboard.W, Keyboard.RightMouseButton). If this new mapping contains inputs which are not defined in the standard, these will be added to the mapping(Keyboard.M). */ myFirstMapping = function() { return { - "name": "example", + "name": "controllerMapping_First", "channels": [ - { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, - { "from": "Keyboard.M", "to": "Actions.YAW_LEFT" }, - - { "from": "Keyboard.RightMouseButton", "to": "Actions.YAW_RIGHT" } - ] -} -} -/* -This JSON is in charge to modify the Standard controller mapping. -The standard controller is an abstraction: all the actual controllers are mapped to it. (i.e. Hydra --mapped to-> Standard --mapped to-> Action) -These new inputs will overwrite the standard ones. -It's possible to find all the standard inputs (and their mapping) into standard.json -To try the mySecondMapping effect you need a controller, not the keyboard. -*/ -mySecondMapping = function() { -return { - "name": "example2", - "channels": [ { "from": "Standard.LY", "to": "Actions.Yaw" }, { "from": "Standard.LX", "to": "Actions.Yaw" }, + + { "from": "Keyboard.W", "to": "Actions.YAW_LEFT" }, + { "from": "Keyboard.M", "to": "Actions.YAW_RIGHT" }, + + { "from": "Keyboard.LeftMouseButton", "to": "Actions.Up" } + ] } } -var tryFirst = true; + +var firstWay = true; var mapping; -if(tryFirst){ +var MAPPING_NAME; + + +if(firstWay){ var myFirstMappingJSON = myFirstMapping(); print('myfirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); + mapping.enable(); }else{ - var mySecondMappingJSON = mySecondMapping(); - print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); - mapping = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); + MAPPING_NAME = "controllerMapping_Second"; + var mapping2 = Controller.newMapping(MAPPING_NAME); + mapping2.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); + }); + mapping2.from(Controller.Standard.LX).to(Controller.Actions.Yaw); + Controller.enableMapping(MAPPING_NAME); } -mapping.enable(); /* -//-----------------some info prints----------------------- +//-----------------some info prints that you would like to enable----------------------- Object.keys(Controller.Standard).forEach(function (input) { print("Controller.Standard." + input + ":" + Controller.Standard[input]); }); @@ -94,5 +100,9 @@ Controller.hardwareChanged.connect(function () { Script.scriptEnding.connect(function () { - mapping.disable(); + if(firstWay){ + mapping.disable(); + } else { + Controller.disableMapping(MAPPING_NAME); + } }); \ No newline at end of file diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js deleted file mode 100644 index c3e6ea8f3d..0000000000 --- a/examples/controllers/rightClickExample.js +++ /dev/null @@ -1,10 +0,0 @@ -var MAPPING_NAME = "com.highfidelity.rightClickExample"; -var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { - print("Keyboard.RightMouseClicked"); -}); -Controller.enableMapping(MAPPING_NAME); - -Script.scriptEnding.connect(function () { - Controller.disableMapping(MAPPING_NAME); -}); \ No newline at end of file From 4ed8a1e5d116a663bdf3e0642e21a239548999d1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 6 Nov 2015 09:25:53 -0800 Subject: [PATCH 0736/1003] changes to send an update to entity-server when AvatarActionHold releases an entity --- interface/src/avatar/AvatarActionHold.h | 2 +- libraries/entities/src/EntityTree.cpp | 18 ++++++++++++++++-- libraries/entities/src/EntityTree.h | 1 + libraries/physics/src/EntityMotionState.cpp | 11 ++++++++--- libraries/physics/src/EntityMotionState.h | 2 +- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 6badf97e9e..95623d5046 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressLocationEdits() { return true; } + virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); } private: static const uint16_t holdVersion; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 24c13ae28e..8e32158362 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -611,6 +611,16 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit return foundEntity; } +void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties) { + if (properties.simulationOwnerChanged()) { + int simIndex = changedProperties.indexOf("simulationOwner"); + if (simIndex >= 0) { + SimulationOwner simOwner = properties.getSimulationOwner(); + changedProperties[simIndex] = QString("simulationOwner:") + QString::number((int)simOwner.getPriority()); + } + } +} + int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -661,7 +671,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { - qCDebug(entities) << "edit" << entityItemID.toString() << properties.listChangedProperties(); + QList changedProperties = properties.listChangedProperties(); + fixupTerseEditLogging(properties, changedProperties); + qCDebug(entities) << "edit" << entityItemID.toString() << changedProperties; } endLogging = usecTimestampNow(); @@ -689,7 +701,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi qCDebug(entities) << " properties:" << properties; } if (wantTerseEditLogging()) { - qCDebug(entities) << "add" << entityItemID.toString() << properties.listChangedProperties(); + QList changedProperties = properties.listChangedProperties(); + fixupTerseEditLogging(properties, changedProperties); + qCDebug(entities) << "add" << entityItemID.toString() << changedProperties; } endLogging = usecTimestampNow(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1957787a60..c177840199 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -77,6 +77,7 @@ public: virtual bool canProcessVersion(PacketVersion thisVersion) const { return thisVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS; } virtual bool handlesEditPacketType(PacketType packetType) const; + void fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties); virtual int processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 42aaea33c2..c8a0f87b6d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -79,8 +79,13 @@ EntityMotionState::~EntityMotionState() { assert(!_entity); } -void EntityMotionState::updateServerPhysicsVariables() { +void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { assert(entityTreeIsLocked()); + if (_entity->getSimulatorID() == sessionID) { + // don't slam these values if we are the simulation owner + return; + } + _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); @@ -92,7 +97,7 @@ void EntityMotionState::updateServerPhysicsVariables() { // virtual bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) { assert(entityTreeIsLocked()); - updateServerPhysicsVariables(); + updateServerPhysicsVariables(engine->getSessionID()); ObjectMotionState::handleEasyChanges(flags, engine); if (flags & Simulation::DIRTY_SIMULATOR_ID) { @@ -129,7 +134,7 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) // virtual bool EntityMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) { - updateServerPhysicsVariables(); + updateServerPhysicsVariables(engine->getSessionID()); return ObjectMotionState::handleHardAndEasyChanges(flags, engine); } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index f3a2e80070..188e7096b9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -28,7 +28,7 @@ public: EntityMotionState(btCollisionShape* shape, EntityItemPointer item); virtual ~EntityMotionState(); - void updateServerPhysicsVariables(); + void updateServerPhysicsVariables(const QUuid& sessionID); virtual bool handleEasyChanges(uint32_t flags, PhysicsEngine* engine); virtual bool handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine); From 46d9a14951f8452897477a6d47bfd34618eaddbe Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 6 Nov 2015 09:35:20 -0800 Subject: [PATCH 0737/1003] Work in progress, fixing the animation playback --- interface/src/avatar/Avatar.cpp | 11 +++++++++++ interface/src/avatar/Avatar.h | 1 + libraries/avatars/src/AvatarData.cpp | 15 +++++++++++++++ libraries/avatars/src/AvatarData.h | 1 + libraries/avatars/src/Player.cpp | 11 ++++++----- libraries/avatars/src/Recorder.cpp | 2 ++ 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b979334383..a5ae8cddda 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -888,6 +888,17 @@ glm::quat Avatar::getJointRotation(int index) const { return rotation; } +QVector Avatar::getJointTranslations() const { + if (QThread::currentThread() != thread()) { + return AvatarData::getJointTranslations(); + } + QVector jointTranslations(_skeletonModel.getJointStateCount()); + for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) { + _skeletonModel.getJointTranslation(i, jointTranslations[i]); + } + return jointTranslations; +} + glm::vec3 Avatar::getJointTranslation(int index) const { if (QThread::currentThread() != thread()) { return AvatarData::getJointTranslation(index); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 44b5d91015..cb7c533db6 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -103,6 +103,7 @@ public: virtual QVector getJointRotations() const; virtual glm::quat getJointRotation(int index) const; + virtual QVector getJointTranslations() const; virtual glm::vec3 getJointTranslation(int index) const; virtual int getJointIndex(const QString& name) const; virtual QStringList getJointNames() const; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a698c6f374..a13c01901e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1146,6 +1146,21 @@ void AvatarData::setJointRotations(QVector jointRotations) { } } +QVector AvatarData::getJointTranslations() const { + if (QThread::currentThread() != thread()) { + QVector result; + QMetaObject::invokeMethod(const_cast(this), + "getJointTranslations", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVector, result)); + return result; + } + QVector jointTranslations(_jointData.size()); + for (int i = 0; i < _jointData.size(); ++i) { + jointTranslations[i] = _jointData[i].translation; + } + return jointTranslations; +} + void AvatarData::setJointTranslations(QVector jointTranslations) { if (QThread::currentThread() != thread()) { QVector result; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3abd63bf63..da857f2f8a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -260,6 +260,7 @@ public: Q_INVOKABLE virtual QVector getJointRotations() const; Q_INVOKABLE virtual void setJointRotations(QVector jointRotations); + Q_INVOKABLE virtual QVector getJointTranslations() const; Q_INVOKABLE virtual void setJointTranslations(QVector jointTranslations); Q_INVOKABLE virtual void clearJointsData(); diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index a425323a41..bb98f51139 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -247,19 +247,20 @@ void Player::play() { _frameInterpolationFactor); _avatar->setTargetScale(context->scale * scale); - + float animFactor = 0.0f; + QVector jointRotations(currentFrame.getJointRotations().size()); for (int i = 0; i < currentFrame.getJointRotations().size(); ++i) { jointRotations[i] = safeMix(currentFrame.getJointRotations()[i], nextFrame.getJointRotations()[i], - _frameInterpolationFactor); + animFactor); } QVector jointTranslations(currentFrame.getJointTranslations().size()); for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) { - jointTranslations[i] = - currentFrame.getJointTranslations()[i] * (1.0f - _frameInterpolationFactor) + - nextFrame.getJointTranslations()[i] * _frameInterpolationFactor; + jointTranslations[i] = glm::mix(currentFrame.getJointTranslations()[i], + nextFrame.getJointTranslations()[i], + animFactor); } _avatar->setJointRotations(jointRotations); diff --git a/libraries/avatars/src/Recorder.cpp b/libraries/avatars/src/Recorder.cpp index 8a90500f00..eeaa8cb1fd 100644 --- a/libraries/avatars/src/Recorder.cpp +++ b/libraries/avatars/src/Recorder.cpp @@ -101,6 +101,7 @@ void Recorder::record() { RecordingFrame frame; frame.setBlendshapeCoefficients(_avatar->getHeadData()->getBlendshapeCoefficients()); frame.setJointRotations(_avatar->getJointRotations()); + frame.setJointTranslations(_avatar->getJointTranslations()); frame.setTranslation(context.orientationInv * (_avatar->getPosition() - context.position)); frame.setRotation(context.orientationInv * _avatar->getOrientation()); frame.setScale(_avatar->getTargetScale() / context.scale); @@ -124,6 +125,7 @@ void Recorder::record() { qCDebug(avatars) << "Recording frame #" << _recording->getFrameNumber(); qCDebug(avatars) << "Blendshapes:" << frame.getBlendshapeCoefficients().size(); qCDebug(avatars) << "JointRotations:" << frame.getJointRotations().size(); + qCDebug(avatars) << "JointRotations:" << frame.getJointTranslations().size(); qCDebug(avatars) << "Translation:" << frame.getTranslation(); qCDebug(avatars) << "Rotation:" << frame.getRotation(); qCDebug(avatars) << "Scale:" << frame.getScale(); From c53c0ec53f92210393ababa7e247d7d897ebf8a7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 11:13:10 -0800 Subject: [PATCH 0738/1003] Fix double delete on shutdown --- interface/src/Application.cpp | 25 +++++++++---------- libraries/plugins/src/plugins/Forward.h | 13 +++++----- libraries/plugins/src/plugins/PluginManager.h | 2 ++ tests/controllers/src/main.cpp | 3 +-- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718d06c6e5..6af4f0c2d1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1027,10 +1027,7 @@ void Application::initializeUi() { foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { QString name = inputPlugin->getName(); if (name == KeyboardMouseDevice::NAME) { - 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); + _keyboardMouseDevice = std::dynamic_pointer_cast(inputPlugin); } } updateInputModes(); @@ -4645,7 +4642,7 @@ DisplayPlugin* Application::getActiveDisplayPlugin() { updateDisplayMode(); Q_ASSERT(_displayPlugin); } - return _displayPlugin.data(); + return _displayPlugin.get(); } const DisplayPlugin* Application::getActiveDisplayPlugin() const { @@ -4685,10 +4682,10 @@ void Application::updateDisplayMode() { bool first = true; foreach(auto displayPlugin, displayPlugins) { addDisplayPluginToMenu(displayPlugin, first); - QObject::connect(displayPlugin.data(), &DisplayPlugin::requestRender, [this] { + QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, [this] { paintGL(); }); - QObject::connect(displayPlugin.data(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { + QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { resizeGL(); }); @@ -4814,12 +4811,14 @@ void Application::updateInputModes() { foreach(auto inputPlugin, inputPlugins) { QString name = inputPlugin->getName(); QAction* action = menu->getActionForOption(name); - if (action->isChecked() && !_activeInputPlugins.contains(inputPlugin)) { - _activeInputPlugins.append(inputPlugin); - newInputPlugins.append(inputPlugin); - } else if (!action->isChecked() && _activeInputPlugins.contains(inputPlugin)) { - _activeInputPlugins.removeOne(inputPlugin); - removedInputPlugins.append(inputPlugin); + + auto it = std::find(std::begin(_activeInputPlugins), std::end(_activeInputPlugins), inputPlugin); + if (action->isChecked() && it == std::end(_activeInputPlugins)) { + _activeInputPlugins.push_back(inputPlugin); + newInputPlugins.push_back(inputPlugin); + } else if (!action->isChecked() && it != std::end(_activeInputPlugins)) { + _activeInputPlugins.erase(it); + removedInputPlugins.push_back(inputPlugin); } } diff --git a/libraries/plugins/src/plugins/Forward.h b/libraries/plugins/src/plugins/Forward.h index 78ec8fdcb3..8d8259ba4f 100644 --- a/libraries/plugins/src/plugins/Forward.h +++ b/libraries/plugins/src/plugins/Forward.h @@ -7,9 +7,8 @@ // #pragma once -#include -#include -#include +#include +#include class DisplayPlugin; class InputPlugin; @@ -17,8 +16,8 @@ class Plugin; class PluginContainer; class PluginManager; -using DisplayPluginPointer = QSharedPointer; -using DisplayPluginList = QVector; -using InputPluginPointer = QSharedPointer; -using InputPluginList = QVector; +using DisplayPluginPointer = std::shared_ptr; +using DisplayPluginList = std::vector; +using InputPluginPointer = std::shared_ptr; +using InputPluginList = std::vector; diff --git a/libraries/plugins/src/plugins/PluginManager.h b/libraries/plugins/src/plugins/PluginManager.h index 17619a93c0..2e056414ec 100644 --- a/libraries/plugins/src/plugins/PluginManager.h +++ b/libraries/plugins/src/plugins/PluginManager.h @@ -7,6 +7,8 @@ // #pragma once +#include + #include "Forward.h" class PluginManager : public QObject { diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 139d9b282c..6e2732f1d8 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,8 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - auto keyboardMouseDevice = static_cast(inputPlugin.data()); // TODO: this seems super hacky - userInputMapper->registerDevice(std::shared_ptr(keyboardMouseDevice)); + userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)); } inputPlugin->pluginUpdate(0, false); } From e630f3072e6bdc4b4da6ffbf03014ffd4bebcc67 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 11:14:10 -0800 Subject: [PATCH 0739/1003] Bit of cleanup --- libraries/controllers/src/controllers/ScriptingInterface.cpp | 2 +- libraries/controllers/src/controllers/UserInputMapper.h | 2 +- libraries/input-plugins/src/input-plugins/InputPlugin.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index a62172a730..bc4b0469f5 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -206,7 +206,7 @@ namespace controller { void ScriptingInterface::updateMaps() { QVariantMap newHardware; auto userInputMapper = DependencyManager::get(); - auto devices = userInputMapper->getDevices(); + const auto& devices = userInputMapper->getDevices(); for (const auto& deviceMapping : devices) { auto deviceID = deviceMapping.first; if (deviceID != userInputMapper->getStandardDeviceID()) { diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index c1dfcf5d33..d93a93016c 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -100,7 +100,7 @@ namespace controller { void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; } glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; } - DevicesMap getDevices() { return _registeredDevices; } + const DevicesMap& getDevices() { return _registeredDevices; } uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } diff --git a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp index 227bd12e1b..6db35572b5 100644 --- a/libraries/input-plugins/src/input-plugins/InputPlugin.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPlugin.cpp @@ -29,7 +29,7 @@ InputPluginList getInputPlugins() { InputPluginList result; for (int i = 0; PLUGIN_POOL[i]; ++i) { - InputPlugin * plugin = PLUGIN_POOL[i]; + InputPlugin* plugin = PLUGIN_POOL[i]; if (plugin->isSupported()) { plugin->init(); result.push_back(InputPluginPointer(plugin)); From 0bf29a441f2bade95193f6d7bc2052e6091e66a6 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 18:41:54 -0800 Subject: [PATCH 0740/1003] Add recording classes --- interface/CMakeLists.txt | 2 +- libraries/recording/src/recording/Clip.cpp | 48 ++++++++ libraries/recording/src/recording/Clip.h | 48 ++++++++ libraries/recording/src/recording/Deck.cpp | 9 ++ libraries/recording/src/recording/Deck.h | 37 ++++++ libraries/recording/src/recording/Forward.h | 18 ++- libraries/recording/src/recording/Frame.cpp | 103 +++++++++++++++++ libraries/recording/src/recording/Frame.h | 40 +++++++ .../recording/src/recording/Recorder.cpp | 62 +++++++++++ libraries/recording/src/recording/Recorder.h | 55 +++++++++ .../src/recording/impl/BufferClip.cpp | 74 ++++++++++++ .../recording/src/recording/impl/BufferClip.h | 47 ++++++++ .../recording/src/recording/impl/FileClip.cpp | 105 ++++++++++++++++++ .../recording/src/recording/impl/FileClip.h | 62 +++++++++++ tests/recording/CMakeLists.txt | 10 ++ tests/recording/src/Constants.h | 19 ++++ tests/recording/src/FrameTests.cpp | 29 +++++ tests/recording/src/FrameTests.h | 21 ++++ tests/recording/src/RecorderTests.cpp | 25 +++++ tests/recording/src/RecorderTests.h | 21 ++++ 20 files changed, 832 insertions(+), 3 deletions(-) create mode 100644 libraries/recording/src/recording/Clip.cpp create mode 100644 libraries/recording/src/recording/Clip.h create mode 100644 libraries/recording/src/recording/Deck.cpp create mode 100644 libraries/recording/src/recording/Deck.h create mode 100644 libraries/recording/src/recording/Frame.cpp create mode 100644 libraries/recording/src/recording/Frame.h create mode 100644 libraries/recording/src/recording/Recorder.cpp create mode 100644 libraries/recording/src/recording/Recorder.h create mode 100644 libraries/recording/src/recording/impl/BufferClip.cpp create mode 100644 libraries/recording/src/recording/impl/BufferClip.h create mode 100644 libraries/recording/src/recording/impl/FileClip.cpp create mode 100644 libraries/recording/src/recording/impl/FileClip.h create mode 100644 tests/recording/CMakeLists.txt create mode 100644 tests/recording/src/Constants.h create mode 100644 tests/recording/src/FrameTests.cpp create mode 100644 tests/recording/src/FrameTests.h create mode 100644 tests/recording/src/RecorderTests.cpp create mode 100644 tests/recording/src/RecorderTests.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 930bdbd7ce..98a9dad909 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -102,7 +102,7 @@ endif() # link required hifi libraries link_hifi_libraries(shared octree environment gpu gl procedural model render - fbx networking model-networking entities avatars + recording fbx networking model-networking entities avatars audio audio-client animation script-engine physics render-utils entities-renderer ui auto-updater controllers plugins display-plugins input-plugins ) diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp new file mode 100644 index 0000000000..ef59532f09 --- /dev/null +++ b/libraries/recording/src/recording/Clip.cpp @@ -0,0 +1,48 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "Clip.h" + +#include "Frame.h" + +#include "impl/FileClip.h" +#include "impl/BufferClip.h" + +using namespace recording; + +Clip::Pointer Clip::fromFile(const QString& filePath) { + return std::make_shared(filePath); +} + +void Clip::toFile(Clip::Pointer clip, const QString& filePath) { + // FIXME +} + +Clip::Pointer Clip::duplicate() { + Clip::Pointer result = std::make_shared(); + + float currentPosition = position(); + seek(0); + + Frame::Pointer frame = nextFrame(); + while (frame) { + result->appendFrame(frame); + } + + seek(currentPosition); + return result; +} + +#if 0 +Clip::Pointer Clip::fromIODevice(QIODevice * device) { + return std::make_shared(device); +} + +void Clip::fromIODevice(Clip::Pointer clip, QIODevice * device) { +} +#endif \ No newline at end of file diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h new file mode 100644 index 0000000000..ca77ba8969 --- /dev/null +++ b/libraries/recording/src/recording/Clip.h @@ -0,0 +1,48 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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_Recording_Clip_h +#define hifi_Recording_Clip_h + +#include "Forward.h" + +#include + +class QIODevice; + +namespace recording { + +class Clip : public QObject { +public: + using Pointer = std::shared_ptr; + + Clip(QObject* parent = nullptr) : QObject(parent) {} + virtual ~Clip() {} + + Pointer duplicate(); + + virtual void seek(float offset) = 0; + virtual float position() const = 0; + + virtual FramePointer peekFrame() const = 0; + virtual FramePointer nextFrame() = 0; + virtual void skipFrame() = 0; + virtual void appendFrame(FramePointer) = 0; + + + static Pointer fromFile(const QString& filePath); + static void toFile(Pointer clip, const QString& filePath); + +protected: + virtual void reset() = 0; +}; + +} + +#endif diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp new file mode 100644 index 0000000000..a80fc43204 --- /dev/null +++ b/libraries/recording/src/recording/Deck.cpp @@ -0,0 +1,9 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "Deck.h" diff --git a/libraries/recording/src/recording/Deck.h b/libraries/recording/src/recording/Deck.h new file mode 100644 index 0000000000..2ae8d6a7be --- /dev/null +++ b/libraries/recording/src/recording/Deck.h @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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_Recording_Deck_h +#define hifi_Recording_Deck_h + +#include "Forward.h" + +#include + +class QIODevice; + +namespace recording { + +class Deck : public QObject { +public: + using Pointer = std::shared_ptr; + + Deck(QObject* parent = nullptr) : QObject(parent) {} + virtual ~Deck(); + + // Place a clip on the deck for recording or playback + void queueClip(ClipPointer clip, float timeOffset = 0.0f); + void play(float timeOffset = 0.0f); + void reposition(float timeOffsetDelta); + void setPlaybackSpeed(float rate); +}; + +} + +#endif diff --git a/libraries/recording/src/recording/Forward.h b/libraries/recording/src/recording/Forward.h index 83a89da847..5bd6dd917f 100644 --- a/libraries/recording/src/recording/Forward.h +++ b/libraries/recording/src/recording/Forward.h @@ -15,14 +15,28 @@ namespace recording { +using FrameType = uint16_t; + +struct Frame; + +using FramePointer = std::shared_ptr; + // A recording of some set of state from the application, usually avatar // data + audio for a single person class Clip; -// An interface for interacting with clips, creating them by recording or -// playing them back. Also serialization to and from files / network sources +using ClipPointer = std::shared_ptr; + +// An interface for playing back clips class Deck; +using DeckPointer = std::shared_ptr; + +// An interface for recording a single clip +class Recorder; + +using RecorderPointer = std::shared_ptr; + } #endif diff --git a/libraries/recording/src/recording/Frame.cpp b/libraries/recording/src/recording/Frame.cpp new file mode 100644 index 0000000000..211f192c0a --- /dev/null +++ b/libraries/recording/src/recording/Frame.cpp @@ -0,0 +1,103 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "Frame.h" + +#include + +#include + +using namespace recording; + +// FIXME move to shared +template +class Registry { +public: + using ForwardMap = QMap; + using BackMap = QMap; + static const Key INVALID_KEY = static_cast(-1); + + Key registerValue(const Value& value) { + Locker lock(_mutex); + Key result = INVALID_KEY; + if (_forwardMap.contains(value)) { + result = _forwardMap[value]; + } else { + _forwardMap[value] = result = _nextKey++; + _backMap[result] = value; + } + return result; + } + + Key getKey(const Value& value) { + Locker lock(_mutex); + Key result = INVALID_KEY; + if (_forwardMap.contains(value)) { + result = _forwardMap[value]; + } + return result; + } + + ForwardMap getKeysByValue() { + Locker lock(_mutex); + ForwardMap result = _forwardMap; + return result; + } + + BackMap getValuesByKey() { + Locker lock(_mutex); + BackMap result = _backMap; + return result; + } + +private: + using Mutex = std::mutex; + using Locker = std::unique_lock; + + Mutex _mutex; + + ForwardMap _forwardMap; + BackMap _backMap; + Key _nextKey { 0 }; +}; + +static Registry frameTypes; +static QMap handlerMap; +using Mutex = std::mutex; +using Locker = std::unique_lock; +static Mutex mutex; +static std::once_flag once; + + + +FrameType Frame::registerFrameType(const QString& frameTypeName) { + Locker lock(mutex); + std::call_once(once, [&] { + auto headerType = frameTypes.registerValue("com.highfidelity.recording.Header"); + Q_ASSERT(headerType == Frame::TYPE_HEADER); + }); + return frameTypes.registerValue(frameTypeName); +} + +QMap Frame::getFrameTypes() { + return frameTypes.getKeysByValue(); +} + +QMap Frame::getFrameTypeNames() { + return frameTypes.getValuesByKey(); +} + +Frame::Handler Frame::registerFrameHandler(FrameType type, Handler handler) { + Locker lock(mutex); + Handler result; + if (handlerMap.contains(type)) { + result = handlerMap[type]; + } + handlerMap[type] = handler; + return result; +} diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h new file mode 100644 index 0000000000..2834637a6b --- /dev/null +++ b/libraries/recording/src/recording/Frame.h @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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_Recording_Frame_h +#define hifi_Recording_Frame_h + +#include "Forward.h" + +#include + +#include + +namespace recording { + +struct Frame { +public: + using Pointer = std::shared_ptr; + using Handler = std::function; + + static const FrameType TYPE_INVALID = 0xFFFF; + static const FrameType TYPE_HEADER = 0x0; + FrameType type { TYPE_INVALID }; + float timeOffset { 0 }; + QByteArray data; + + static FrameType registerFrameType(const QString& frameTypeName); + static QMap getFrameTypes(); + static QMap getFrameTypeNames(); + static Handler registerFrameHandler(FrameType type, Handler handler); +}; + +} + +#endif diff --git a/libraries/recording/src/recording/Recorder.cpp b/libraries/recording/src/recording/Recorder.cpp new file mode 100644 index 0000000000..a38e4252b9 --- /dev/null +++ b/libraries/recording/src/recording/Recorder.cpp @@ -0,0 +1,62 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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 "Recorder.h" + +#include + +#include "impl/BufferClip.h" +#include "Frame.h" + +using namespace recording; + +void Recorder::start() { + if (!_recording) { + _recording = true; + if (!_clip) { + _clip = std::make_shared(); + } + _timer.start(); + emit recordingStateChanged(); + } +} + +void Recorder::stop() { + if (!_recording) { + _recording = false; + _elapsed = _timer.elapsed(); + emit recordingStateChanged(); + } +} + +bool Recorder::isRecording() { + return _recording; +} + +void Recorder::clear() { + _clip.reset(); +} + +void Recorder::recordFrame(FrameType type, QByteArray frameData) { + if (!_recording || !_clip) { + return; + } + + Frame::Pointer frame = std::make_shared(); + frame->type = type; + frame->data = frameData; + frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND; + _clip->appendFrame(frame); +} + +ClipPointer Recorder::getClip() { + auto result = _clip; + _clip.reset(); + return result; +} + diff --git a/libraries/recording/src/recording/Recorder.h b/libraries/recording/src/recording/Recorder.h new file mode 100644 index 0000000000..deae543bb0 --- /dev/null +++ b/libraries/recording/src/recording/Recorder.h @@ -0,0 +1,55 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Recording_Recorder_h +#define hifi_Recording_Recorder_h + +#include "Forward.h" + +#include +#include + +namespace recording { + +// An interface for interacting with clips, creating them by recording or +// playing them back. Also serialization to and from files / network sources +class Recorder : public QObject { +public: + using Pointer = std::shared_ptr; + + Recorder(QObject* parent = nullptr) : QObject(parent) {} + virtual ~Recorder(); + + // Start recording frames + void start(); + // Stop recording + void stop(); + // Test if recording is active + bool isRecording(); + // Erase the currently recorded content + void clear(); + + void recordFrame(FrameType type, QByteArray frameData); + + // Return the currently recorded content + ClipPointer getClip(); + +signals: + void recordingStateChanged(); + +private: + QElapsedTimer _timer; + ClipPointer _clip; + quint64 _elapsed; + bool _recording { false }; +}; + +} + +#endif diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp new file mode 100644 index 0000000000..378fee2558 --- /dev/null +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -0,0 +1,74 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "BufferClip.h" + +#include "../Frame.h" + +using namespace recording; + + +void BufferClip::seek(float offset) { + Locker lock(_mutex); + auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, + [](Frame::Pointer a, float b)->bool{ + return a->timeOffset < b; + } + ); + _frameIndex = itr - _frames.begin(); +} + +float BufferClip::position() const { + Locker lock(_mutex); + float result = std::numeric_limits::max(); + if (_frameIndex < _frames.size()) { + result = _frames[_frameIndex]->timeOffset; + } + return result; +} + +FramePointer BufferClip::peekFrame() const { + Locker lock(_mutex); + FramePointer result; + if (_frameIndex < _frames.size()) { + result = _frames[_frameIndex]; + } + return result; +} + +FramePointer BufferClip::nextFrame() { + Locker lock(_mutex); + FramePointer result; + if (_frameIndex < _frames.size()) { + result = _frames[_frameIndex]; + ++_frameIndex; + } + return result; +} + +void BufferClip::appendFrame(FramePointer newFrame) { + auto currentPosition = position(); + seek(newFrame->timeOffset); + { + Locker lock(_mutex); + _frames.insert(_frames.begin() + _frameIndex, newFrame); + } + seek(currentPosition); +} + +void BufferClip::skipFrame() { + Locker lock(_mutex); + if (_frameIndex < _frames.size()) { + ++_frameIndex; + } +} + +void BufferClip::reset() { + Locker lock(_mutex); + _frameIndex = 0; +} diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h new file mode 100644 index 0000000000..8fd78a9091 --- /dev/null +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -0,0 +1,47 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Recording_Impl_BufferClip_h +#define hifi_Recording_Impl_BufferClip_h + +#include "../Clip.h" + +#include + +namespace recording { + +class BufferClip : public Clip { +public: + using Pointer = std::shared_ptr; + + BufferClip(QObject* parent = nullptr) : Clip(parent) {} + virtual ~BufferClip() {} + + virtual void seek(float offset) override; + virtual float position() const override; + + virtual FramePointer peekFrame() const override; + virtual FramePointer nextFrame() override; + virtual void skipFrame() override; + virtual void appendFrame(FramePointer) override; + +private: + using Mutex = std::mutex; + using Locker = std::unique_lock; + + virtual void reset() override; + + std::vector _frames; + mutable Mutex _mutex; + mutable size_t _frameIndex { 0 }; +}; + +} + +#endif diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp new file mode 100644 index 0000000000..354a97c5db --- /dev/null +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -0,0 +1,105 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "FileClip.h" + +#include "../Frame.h" + +#include + +using namespace recording; + +static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t) + 1; + +FileClip::FileClip(const QString& fileName, QObject* parent) : Clip(parent), _file(fileName) { + auto size = _file.size(); + _map = _file.map(0, size, QFile::MapPrivateOption); + + auto current = _map; + auto end = current + size; + // Read all the frame headers + while (end - current < MINIMUM_FRAME_SIZE) { + FrameHeader header; + memcpy(&(header.type), current, sizeof(FrameType)); + current += sizeof(FrameType); + memcpy(&(header.timeOffset), current, sizeof(FrameType)); + current += sizeof(float); + memcpy(&(header.size), current, sizeof(uint16_t)); + current += sizeof(uint16_t); + header.fileOffset = current - _map; + if (end - current < header.size) { + break; + } + + _frameHeaders.push_back(header); + } +} + +FileClip::~FileClip() { + Locker lock(_mutex); + _file.unmap(_map); + _map = nullptr; +} + +void FileClip::seek(float offset) { + Locker lock(_mutex); + auto itr = std::lower_bound(_frameHeaders.begin(), _frameHeaders.end(), offset, + [](const FrameHeader& a, float b)->bool { + return a.timeOffset < b; + } + ); + _frameIndex = itr - _frameHeaders.begin(); +} + +float FileClip::position() const { + Locker lock(_mutex); + float result = std::numeric_limits::max(); + if (_frameIndex < _frameHeaders.size()) { + result = _frameHeaders[_frameIndex].timeOffset; + } + return result; +} + +FramePointer FileClip::readFrame(uint32_t frameIndex) const { + FramePointer result; + if (frameIndex < _frameHeaders.size()) { + result = std::make_shared(); + const FrameHeader& header = _frameHeaders[frameIndex]; + result->type = header.type; + result->timeOffset = header.timeOffset; + result->data.insert(0, reinterpret_cast(_map)+header.fileOffset, header.size); + } + return result; +} + +FramePointer FileClip::peekFrame() const { + Locker lock(_mutex); + return readFrame(_frameIndex); +} + +FramePointer FileClip::nextFrame() { + Locker lock(_mutex); + auto result = readFrame(_frameIndex); + if (_frameIndex < _frameHeaders.size()) { + ++_frameIndex; + } + return result; +} + +void FileClip::skipFrame() { + ++_frameIndex; +} + +void FileClip::reset() { + _frameIndex = 0; +} + +void FileClip::appendFrame(FramePointer) { + throw std::runtime_error("File clips are read only"); +} + diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h new file mode 100644 index 0000000000..9b13adc9ef --- /dev/null +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -0,0 +1,62 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Recording_Impl_FileClip_h +#define hifi_Recording_Impl_FileClip_h + +#include "../Clip.h" + +#include + +#include + +namespace recording { + +class FileClip : public Clip { +public: + using Pointer = std::shared_ptr; + + FileClip(const QString& file, QObject* parent = nullptr); + virtual ~FileClip(); + + virtual void seek(float offset) override; + virtual float position() const override; + + virtual FramePointer peekFrame() const override; + virtual FramePointer nextFrame() override; + virtual void appendFrame(FramePointer) override; + virtual void skipFrame() override; + +private: + using Mutex = std::mutex; + using Locker = std::unique_lock; + + virtual void reset() override; + + struct FrameHeader { + FrameType type; + float timeOffset; + uint16_t size; + quint64 fileOffset; + }; + + using FrameHeaders = std::vector; + + FramePointer readFrame(uint32_t frameIndex) const; + + mutable Mutex _mutex; + QFile _file; + uint32_t _frameIndex { 0 }; + uchar* _map; + FrameHeaders _frameHeaders; +}; + +} + +#endif diff --git a/tests/recording/CMakeLists.txt b/tests/recording/CMakeLists.txt new file mode 100644 index 0000000000..f1e130956d --- /dev/null +++ b/tests/recording/CMakeLists.txt @@ -0,0 +1,10 @@ + +# Declare dependencies +macro (setup_testcase_dependencies) + # link in the shared libraries + link_hifi_libraries(shared recording) + + copy_dlls_beside_windows_executable() +endmacro () + +setup_hifi_testcase() diff --git a/tests/recording/src/Constants.h b/tests/recording/src/Constants.h new file mode 100644 index 0000000000..37758bfd68 --- /dev/null +++ b/tests/recording/src/Constants.h @@ -0,0 +1,19 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Constants_h +#define hifi_Constants_h + +static const QString HEADER_NAME = "com.highfidelity.recording.Header"; +static const QString TEST_NAME = "com.highfidelity.recording.Test"; + +#endif // hifi_FrameTests_h + + diff --git a/tests/recording/src/FrameTests.cpp b/tests/recording/src/FrameTests.cpp new file mode 100644 index 0000000000..f61d45c197 --- /dev/null +++ b/tests/recording/src/FrameTests.cpp @@ -0,0 +1,29 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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 "FrameTests.h" +#include "Constants.h" +#include "../QTestExtensions.h" + +#include + +QTEST_MAIN(FrameTests) + +void FrameTests::registerFrameTypeTest() { + auto result = recording::Frame::registerFrameType(TEST_NAME); + QCOMPARE(result, (recording::FrameType)1); + auto forwardMap = recording::Frame::getFrameTypes(); + QCOMPARE(forwardMap.count(TEST_NAME), 1); + QCOMPARE(forwardMap[TEST_NAME], result); + QCOMPARE(forwardMap[HEADER_NAME], recording::Frame::TYPE_HEADER); + auto backMap = recording::Frame::getFrameTypeNames(); + QCOMPARE(backMap.count(result), 1); + QCOMPARE(backMap[result], TEST_NAME); + QCOMPARE(backMap[recording::Frame::TYPE_HEADER], HEADER_NAME); +} + diff --git a/tests/recording/src/FrameTests.h b/tests/recording/src/FrameTests.h new file mode 100644 index 0000000000..04f73532a5 --- /dev/null +++ b/tests/recording/src/FrameTests.h @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_FrameTests_h +#define hifi_FrameTests_h + +#include + +class FrameTests : public QObject { + Q_OBJECT +private slots: + void registerFrameTypeTest(); +}; + +#endif // hifi_FrameTests_h diff --git a/tests/recording/src/RecorderTests.cpp b/tests/recording/src/RecorderTests.cpp new file mode 100644 index 0000000000..76b4b46577 --- /dev/null +++ b/tests/recording/src/RecorderTests.cpp @@ -0,0 +1,25 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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 "RecorderTests.h" +#include "Constants.h" +#include "../QTestExtensions.h" + +#include + +QTEST_MAIN(RecorderTests) + +void RecorderTests::recorderTest() { + //auto recorder = std::make_shared(); + //QCOMPARE(recoreder.isRecording(), false); + //recorder.start(); + //QCOMPARE(recoreder.isRecording(), true); + //recorder.stop(); + //QCOMPARE(recoreder.isRecording(), false); +} + diff --git a/tests/recording/src/RecorderTests.h b/tests/recording/src/RecorderTests.h new file mode 100644 index 0000000000..8e97a828a2 --- /dev/null +++ b/tests/recording/src/RecorderTests.h @@ -0,0 +1,21 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_RecorderTests_h +#define hifi_RecorderTests_h + +#include + +class RecorderTests : public QObject { + Q_OBJECT +private slots: + void recorderTest(); +}; + +#endif From bc516c0b865c3eab256299fd5e198a5de4c3b2ae Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 6 Nov 2015 11:32:56 -0800 Subject: [PATCH 0741/1003] use render-item status getters to display physics debugging information --- .../src/RenderableBoxEntityItem.cpp | 3 - .../src/RenderableDebugableEntityItem.cpp | 67 ------------------- .../src/RenderableDebugableEntityItem.h | 23 ------- .../src/RenderableLineEntityItem.cpp | 2 - .../src/RenderableLineEntityItem.h | 1 - .../src/RenderableModelEntityItem.cpp | 27 ++++++-- .../src/RenderableModelEntityItem.h | 1 - .../src/RenderablePolyLineEntityItem.cpp | 2 - .../src/RenderablePolyLineEntityItem.h | 1 - .../src/RenderablePolyVoxEntityItem.cpp | 2 - .../src/RenderablePolyVoxEntityItem.h | 1 - .../src/RenderableSphereEntityItem.cpp | 4 -- 12 files changed, 21 insertions(+), 113 deletions(-) delete mode 100644 libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp delete mode 100644 libraries/entities-renderer/src/RenderableDebugableEntityItem.h diff --git a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp index 077f28350b..5d82311bcc 100644 --- a/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableBoxEntityItem.cpp @@ -20,7 +20,6 @@ #include #include -#include "RenderableDebugableEntityItem.h" #include "../render-utils/simple_vert.h" #include "../render-utils/simple_frag.h" @@ -63,6 +62,4 @@ void RenderableBoxEntityItem::render(RenderArgs* args) { } else { DependencyManager::get()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor); } - - RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp b/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp deleted file mode 100644 index f103aaed4c..0000000000 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// RenderableDebugableEntityItem.cpp -// libraries/entities-renderer/src/ -// -// Created by Seth Alves on 5/1/15. -// 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 "RenderableDebugableEntityItem.h" - -#include - -#include -#include -#include - - -void RenderableDebugableEntityItem::renderBoundingBox(EntityItem* entity, RenderArgs* args, - float puffedOut, glm::vec4& color) { - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - - auto shapeTransform = entity->getTransformToCenter(); - if (puffedOut != 0.0f) { - shapeTransform.postScale(1.0f + puffedOut); - } - batch.setModelTransform(Transform()); // we want to include the scale as well - DependencyManager::get()->renderWireCubeInstance(batch, shapeTransform, color); -} - -void RenderableDebugableEntityItem::render(EntityItem* entity, RenderArgs* args) { - if (args->_debugFlags & RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP) { - Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - - batch.setModelTransform(entity->getTransformToCenter()); // we want to include the scale as well - - auto nodeList = DependencyManager::get(); - const QUuid& myNodeID = nodeList->getSessionUUID(); - bool highlightSimulationOwnership = (entity->getSimulatorID() == myNodeID); - if (highlightSimulationOwnership) { - glm::vec4 greenColor(0.0f, 1.0f, 0.2f, 1.0f); - renderBoundingBox(entity, args, 0.08f, greenColor); - } - - quint64 now = usecTimestampNow(); - if (now - entity->getLastEditedFromRemote() < 0.1f * USECS_PER_SECOND) { - glm::vec4 redColor(1.0f, 0.0f, 0.0f, 1.0f); - renderBoundingBox(entity, args, 0.16f, redColor); - } - - if (now - entity->getLastBroadcast() < 0.2f * USECS_PER_SECOND) { - glm::vec4 yellowColor(1.0f, 1.0f, 0.2f, 1.0f); - renderBoundingBox(entity, args, 0.24f, yellowColor); - } - - ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState && motionState->isActive()) { - glm::vec4 blueColor(0.0f, 0.0f, 1.0f, 1.0f); - renderBoundingBox(entity, args, 0.32f, blueColor); - } - } -} diff --git a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h b/libraries/entities-renderer/src/RenderableDebugableEntityItem.h deleted file mode 100644 index 2680d882f5..0000000000 --- a/libraries/entities-renderer/src/RenderableDebugableEntityItem.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// RenderableDebugableEntityItem.h -// libraries/entities-renderer/src/ -// -// Created by Seth Alves on 5/1/15. -// 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_RenderableDebugableEntityItem_h -#define hifi_RenderableDebugableEntityItem_h - -#include - -class RenderableDebugableEntityItem { -public: - static void renderBoundingBox(EntityItem* entity, RenderArgs* args, float puffedOut, glm::vec4& color); - static void render(EntityItem* entity, RenderArgs* args); -}; - -#endif // hifi_RenderableDebugableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 3735690c33..f39c31e22b 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -54,6 +54,4 @@ void RenderableLineEntityItem::render(RenderArgs* args) { DependencyManager::get()->bindSimpleProgram(batch); DependencyManager::get()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); } - - RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h index 09f98ca364..ba990046a0 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -13,7 +13,6 @@ #define hifi_RenderableLineEntityItem_h #include -#include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" #include diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index e604bdb925..8543c00eec 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" @@ -186,15 +187,28 @@ void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item float normalizedDelta = delta * WAIT_THRESHOLD_INV; // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD // Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green) - return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : render::Item::Status::Value::RED)); + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? + render::Item::Status::Value::GREEN : + render::Item::Status::Value::RED)); }); + statusGetters.push_back([entity] () -> render::Item::Status::Value { quint64 delta = usecTimestampNow() - entity->getLastBroadcast(); const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND); float normalizedDelta = delta * WAIT_THRESHOLD_INV; // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD // Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green) - return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : render::Item::Status::Value::CYAN)); + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? + render::Item::Status::Value::MAGENTA : + render::Item::Status::Value::CYAN)); + }); + + statusGetters.push_back([entity] () -> render::Item::Status::Value { + ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState && motionState->isActive()) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE); + } + return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE); }); } @@ -320,11 +334,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } } } else { - glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); - RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor); + static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); + gpu::Batch& batch = *args->_batch; + auto shapeTransform = getTransformToCenter(); + batch.setModelTransform(Transform()); // we want to include the scale as well + DependencyManager::get()->renderWireCubeInstance(batch, shapeTransform, greenColor); } - - RenderableDebugableEntityItem::render(this, args); } Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 4dc1cced48..04a1694dd3 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -16,7 +16,6 @@ #include #include -#include "RenderableDebugableEntityItem.h" class Model; class EntityTreeRenderer; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 7bec8f2b03..32418199b8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -153,6 +153,4 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); - - RenderableDebugableEntityItem::render(this, args); }; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 2832053639..c8a47cce0c 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -14,7 +14,6 @@ #include #include -#include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" #include #include diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index fd5a9a6b4a..cd88638e51 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -538,8 +538,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { batch._glUniform3f(voxelVolumeSizeLocation, _voxelVolumeSize.x, _voxelVolumeSize.y, _voxelVolumeSize.z); batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); - - RenderableDebugableEntityItem::render(this, args); } bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index ef44ba5ab0..9d0931a47e 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -21,7 +21,6 @@ #include #include "PolyVoxEntityItem.h" -#include "RenderableDebugableEntityItem.h" #include "RenderableEntityItem.h" #include "gpu/Context.h" diff --git a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp index 246cd2fea7..0400ecb999 100644 --- a/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableSphereEntityItem.cpp @@ -20,7 +20,6 @@ #include #include -#include "RenderableDebugableEntityItem.h" #include "../render-utils/simple_vert.h" #include "../render-utils/simple_frag.h" @@ -70,7 +69,4 @@ void RenderableSphereEntityItem::render(RenderArgs* args) { batch.setModelTransform(Transform()); DependencyManager::get()->renderSolidSphereInstance(batch, modelTransform, sphereColor); } - - - RenderableDebugableEntityItem::render(this, args); }; From e928c8278b2acfbb968a661b6558e63d2eaba734 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 11:48:51 -0800 Subject: [PATCH 0742/1003] overload a single function and cleanup, add examples --- examples/entityScripts/createParamsEntity.js | 43 ++++++++++++++++++ examples/entityScripts/paramsEntity.js | 45 +++++++++++++++++++ .../src/EntitiesScriptEngineProvider.h | 3 +- .../entities/src/EntityScriptingInterface.cpp | 7 --- .../entities/src/EntityScriptingInterface.h | 4 +- libraries/script-engine/src/ScriptEngine.cpp | 31 ------------- libraries/script-engine/src/ScriptEngine.h | 3 +- 7 files changed, 92 insertions(+), 44 deletions(-) create mode 100644 examples/entityScripts/createParamsEntity.js create mode 100644 examples/entityScripts/paramsEntity.js diff --git a/examples/entityScripts/createParamsEntity.js b/examples/entityScripts/createParamsEntity.js new file mode 100644 index 0000000000..e01f075716 --- /dev/null +++ b/examples/entityScripts/createParamsEntity.js @@ -0,0 +1,43 @@ +// +// createParamsEntity.js +// +// +// Additions by James B. Pollack @imgntn on 11/6/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This script demonstrates creates an entity and sends it a method call with parameters. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var PARAMS_SCRIPT_URL = Script.resolvePath('paramsEntity.js'); + +var testEntity = Entities.addEntity({ + name: 'paramsTestEntity', + dimensions: { + x: 1, + y: 1, + z: 1 + }, + type: 'Box', + position: { + x: 0, + y: 0, + z: 0 + }, + visible: false, + script: PARAMS_SCRIPT_URL +}); + + +var subData1 = ['apple', 'banana', 'orange']; +var subData2 = { + thing: 1, + otherThing: 2 +}; +var data = [subData1, JSON.stringify(subData2), 'third']; +Script.setTimeout(function() { + print('sending data to entity') + Entities.callEntityMethod(testEntity, 'testParams', data); +}, 1500) \ No newline at end of file diff --git a/examples/entityScripts/paramsEntity.js b/examples/entityScripts/paramsEntity.js new file mode 100644 index 0000000000..afcfd279d5 --- /dev/null +++ b/examples/entityScripts/paramsEntity.js @@ -0,0 +1,45 @@ +// +// paramsEntity.js +// +// Script Type: Entity +// +// Additions by James B. Pollack @imgntn on 11/6/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This script demonstrates how to recieve parameters from a Entities.callEntityMethod call +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function() { + + function ParamsEntity() { + return; + } + + ParamsEntity.prototype = { + preload: function(entityID) { + print('entity loaded') + this.entityID = entityID; + }, + testParams: function(myID, paramsArray) { + + paramsArray.forEach(function(param) { + var p; + try { + p = JSON.parse(param); + print("it's a json param") + print('json param property:' + p.thing); + } catch (err) { + print('not a json param') + p = param; + print('param is:' + p); + } + + }); + + } + + } + + return new ParamsEntity(); +}); \ No newline at end of file diff --git a/libraries/entities/src/EntitiesScriptEngineProvider.h b/libraries/entities/src/EntitiesScriptEngineProvider.h index 1922f660cd..69bf73e688 100644 --- a/libraries/entities/src/EntitiesScriptEngineProvider.h +++ b/libraries/entities/src/EntitiesScriptEngineProvider.h @@ -19,8 +19,7 @@ class EntitiesScriptEngineProvider { public: - virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) = 0; - virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) = 0; + virtual void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()) = 0; }; #endif // hifi_EntitiesScriptEngineProvider_h \ No newline at end of file diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a68f946173..8ca0e9b5fa 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -217,13 +217,6 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } } -void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method) { - if (_entitiesScriptEngine) { - EntityItemID entityID{ id }; - _entitiesScriptEngine->callEntityScriptMethod(entityID, method); - } -} - void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { if (_entitiesScriptEngine) { EntityItemID entityID{ id }; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index afec8e3680..8a4414a596 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -94,8 +94,8 @@ public slots: /// Allows a script to call a method on an entity's script. The method will execute in the entity script /// engine. If the entity does not have an entity script or the method does not exist, this call will have /// no effect. - Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method); - Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params); + Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); + /// finds the closest model to the center point, within the radius /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f64efa8c1c..e923ea6d21 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1170,37 +1170,6 @@ void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { recurseGuard = false; } - -void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName) { - if (QThread::currentThread() != thread()) { - #ifdef THREAD_DEBUGGING - qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName; - #endif - - QMetaObject::invokeMethod(this, "callEntityScriptMethod", - Q_ARG(const EntityItemID&, entityID), - Q_ARG(const QString&, methodName)); - return; - } - #ifdef THREAD_DEBUGGING - qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName; - #endif - - refreshFileScript(entityID); - if (_entityScripts.contains(entityID)) { - EntityScriptDetails details = _entityScripts[entityID]; - QScriptValue entityScript = details.scriptObject; // previously loaded - if (entityScript.property(methodName).isFunction()) { - QScriptValueList args; - args << entityID.toScriptValue(this); - entityScript.property(methodName).call(entityScript, args); - } - - } -} - void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 447a22fe5e..c957b0c3b4 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -116,8 +116,7 @@ public: Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload = false); // will call the preload method once loaded Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName); - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params); + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); From cf86d27417716ca48a3dd7805730870e920f30e8 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 11:50:34 -0800 Subject: [PATCH 0743/1003] cleanup headers --- examples/entityScripts/createParamsEntity.js | 5 ++--- examples/entityScripts/paramsEntity.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/entityScripts/createParamsEntity.js b/examples/entityScripts/createParamsEntity.js index e01f075716..991bb0d667 100644 --- a/examples/entityScripts/createParamsEntity.js +++ b/examples/entityScripts/createParamsEntity.js @@ -1,11 +1,10 @@ // // createParamsEntity.js // -// -// Additions by James B. Pollack @imgntn on 11/6/2015 +// Created by James B. Pollack @imgntn on 11/6/2015 // Copyright 2015 High Fidelity, Inc. // -// This script demonstrates creates an entity and sends it a method call with parameters. +// This script demonstrates creating an entity and sending it a method call with parameters. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/examples/entityScripts/paramsEntity.js b/examples/entityScripts/paramsEntity.js index afcfd279d5..6cb5cb5833 100644 --- a/examples/entityScripts/paramsEntity.js +++ b/examples/entityScripts/paramsEntity.js @@ -3,7 +3,7 @@ // // Script Type: Entity // -// Additions by James B. Pollack @imgntn on 11/6/2015 +// Created by James B. Pollack @imgntn on 11/6/2015 // Copyright 2015 High Fidelity, Inc. // // This script demonstrates how to recieve parameters from a Entities.callEntityMethod call From 46b7b92b2f76aded24a7134b9afa455197b12c5a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 6 Nov 2015 11:54:51 -0800 Subject: [PATCH 0744/1003] prefix private member variables with _ --- interface/src/avatar/AvatarActionHold.cpp | 6 +++--- interface/src/avatar/AvatarActionHold.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 6078d326e0..866f444b32 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -109,10 +109,10 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { if (_previousSet) { // smooth velocity over 2 frames glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget; - glm::vec3 positionalVelocity = (positionalDelta + previousPositionalDelta) / (deltaTimeStep + previousDeltaTimeStep); + glm::vec3 positionalVelocity = (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); - previousPositionalDelta = positionalDelta; - previousDeltaTimeStep = deltaTimeStep; + _previousPositionalDelta = positionalDelta; + _previousDeltaTimeStep = deltaTimeStep; } } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 4856a17b89..eb8ebf2525 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -47,8 +47,8 @@ private: glm::vec3 _previousPositionalTarget; glm::quat _previousRotationalTarget; - float previousDeltaTimeStep = 0.0f; - glm::vec3 previousPositionalDelta; + float _previousDeltaTimeStep = 0.0f; + glm::vec3 _previousPositionalDelta; }; #endif // hifi_AvatarActionHold_h From 6d3a79af83e754c107a2f6b488b805395bd0cd2c Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 6 Nov 2015 12:46:08 -0800 Subject: [PATCH 0745/1003] changed the behaviour of Standard.LY input --- interface/resources/controllers/standard.json | 13 +++++++++++-- interface/src/Application.cpp | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5de188c0b6..8a24543b74 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -14,8 +14,17 @@ ] }, { "from": "Standard.RX", "to": "Actions.Yaw" }, - { "from": "Standard.RY", "when": "!Application.InHMD", "to": "Actions.Pitch" }, - + + { "from": "Standard.RY", + "when": "Application.Grounded", + "to": "Actions.Up", + "filters": + [ + { "type": "deadZone", "min": 0.95 }, + "invert" + ] + }, + { "from": "Standard.RY", "to": "Actions.Up", "filters": "invert"}, { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718d06c6e5..571c6ca6af 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -646,6 +646,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); })); + _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { + return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); + })); userInputMapper->registerDevice(_applicationStateDevice); From 1d41ef868b620e8e12aecc8a76deaaddbd7a5a3c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 13:11:06 -0800 Subject: [PATCH 0746/1003] update bubblewand model refs --- examples/toybox/bubblewand/createWand.js | 11 ++++++++--- examples/toybox/bubblewand/wand.js | 2 +- unpublishedScripts/hiddenEntityReset.js | 5 ++--- unpublishedScripts/masterReset.js | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/toybox/bubblewand/createWand.js b/examples/toybox/bubblewand/createWand.js index 25649a9aad..d62c2064cf 100644 --- a/examples/toybox/bubblewand/createWand.js +++ b/examples/toybox/bubblewand/createWand.js @@ -13,13 +13,18 @@ Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); -var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; -var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/collisionHull.obj'; +var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; +var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; + var WAND_SCRIPT_URL = Script.resolvePath("wand.js"); //create the wand in front of the avatar -var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); +var center = Vec3.sum(Vec3.sum(MyAvatar.position, { + x: 0, + y: 0.5, + z: 0 +}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation()))); var wand = Entities.addEntity({ name: 'Bubble Wand', diff --git a/examples/toybox/bubblewand/wand.js b/examples/toybox/bubblewand/wand.js index 707b0fd47f..c8ba51f51d 100644 --- a/examples/toybox/bubblewand/wand.js +++ b/examples/toybox/bubblewand/wand.js @@ -17,7 +17,7 @@ Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); - var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/james/bubblewand/models/bubble/bubble.fbx"; + var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx"; var BUBBLE_INITIAL_DIMENSIONS = { x: 0.01, diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index d343c52497..ef0557a54f 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -927,9 +927,8 @@ } function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; - + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; var entity = Entities.addEntity({ name: 'Bubble Wand', type: "Model", diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 364807f42f..a8e156617a 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -909,8 +909,8 @@ MasterReset = function() { } function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/james/bubblewand/models/wand/actual_no_top_collision_hull.obj'; + var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; + var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; var entity = Entities.addEntity({ name: 'Bubble Wand', From a432a62deecf405ad1f53911a479feb07d3a0001 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 6 Nov 2015 13:53:25 -0800 Subject: [PATCH 0747/1003] model swapping --- examples/marketplace/marketplaceSpawner.js | 68 ++++++++++++++++------ examples/marketplace/modelSwap.js | 41 +++++++++++++ 2 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 examples/marketplace/modelSwap.js diff --git a/examples/marketplace/marketplaceSpawner.js b/examples/marketplace/marketplaceSpawner.js index 3e655e3e74..6799ed63a3 100644 --- a/examples/marketplace/marketplaceSpawner.js +++ b/examples/marketplace/marketplaceSpawner.js @@ -1,12 +1,17 @@ var floorPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));; floorPosition.y = MyAvatar.position.y - 5; -var modelsToLoad = [ - { - lowURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_low.fbx", - highURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_hi.fbx" - } -]; +Script.include('../libraries/utils.js'); + +var entityScriptURL = Script.resolvePath("modelSwap.js"); + +var modelsToLoad = [{ + lowURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/dojo/dojo_low.fbx", + highURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/dojo/dojo_hi.fbx" +}, { + lowURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_low.fbx", + highURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_hi.fbx" +}]; var models = []; @@ -15,35 +20,62 @@ var floor = Entities.addEntity({ modelURL: "https://hifi-public.s3.amazonaws.com/ozan/3d_marketplace/props/floor/3d_mp_floor.fbx", position: floorPosition, shapeType: 'box', - dimensions: {x: 1000, y: 9, z: 1000} + dimensions: { + x: 1000, + y: 9, + z: 1000 + } }); //Create grid var modelParams = { type: "Model", - shapeType: "box", - dimensions: {x: 53, y: 15.7, z: 44.8}, - velocity: {x: 0, y: -1, z: 0}, - gravity: {x: 0, y: -1, z: 0}, - collisionsWillMove: true + dimensions: { + x: 31.85, + y: 7.75, + z: 54.51 + }, + script: entityScriptURL, + userData: JSON.stringify({ + grabbableKey: { + wantsTrigger: true + } + }) + }; -var modelPosition = {x: floorPosition.x + 10, y: floorPosition.y + 15, z: floorPosition.z}; +var modelPosition = { + x: floorPosition.x + 10, + y: floorPosition.y + 8.5, + z: floorPosition.z - 30 +}; for (var i = 0; i < modelsToLoad.length; i++) { modelParams.modelURL = modelsToLoad[i].lowURL; - modelPosition.z -= 10; modelParams.position = modelPosition; - var model = Entities.addEntity(modelParams); - models.push(model); -} + var lowModel = Entities.addEntity(modelParams); + modelParams.modelURL = modelsToLoad[i].highURL; + modelParams.visible = false; + modelParams.dimensions = Vec3.multiply(modelParams.dimensions, 0.5); + var highModel = Entities.addEntity(modelParams); + models.push({ + low: lowModel, + high: highModel + }); + // customKey, id, data + setEntityCustomData('modelCounterpart', lowModel, {modelCounterpartId: highModel}); + setEntityCustomData('modelCounterpart', highModel, {modelCounterpartId: lowModel}); + + modelPosition.z -= 60; +} function cleanup() { Entities.deleteEntity(floor); models.forEach(function(model) { - Entities.deleteEntity(model); + Entities.deleteEntity(model.low); + Entities.deleteEntity(model.high); }); } diff --git a/examples/marketplace/modelSwap.js b/examples/marketplace/modelSwap.js new file mode 100644 index 0000000000..29dbc84e98 --- /dev/null +++ b/examples/marketplace/modelSwap.js @@ -0,0 +1,41 @@ +// When user holds down trigger on model for enough time, the model with do a cool animation and swap out with the low or high version of it + + + +(function() { + + var _this; + ModelSwaper = function() { + _this = this; + }; + + ModelSwaper.prototype = { + + startFarTrigger: function() { + print("START TRIGGER") + + //make self invisible and make the model's counterpart visible! + var dimensions = Entities.getEntityProperties(this.entityID, "dimensions").dimensions; + Entities.editEntity(this.entityID, { + visible: false, + dimensions: Vec3.multiply(dimensions, 0.5) + }); + dimensions = Entities.getEntityProperties(this.modelCounterpartId, "dimensions").dimensions; + Entities.editEntity(this.modelCounterpartId, { + visible: true, + dimensions: Vec3.multiply(dimensions, 2) + }); + + }, + + preload: function(entityID) { + this.entityID = entityID; + var props = Entities.getEntityProperties(this.entityID, ["userData"]); + this.modelCounterpartId = JSON.parse(props.userData).modelCounterpart.modelCounterpartId; + } + + }; + + // entity scripts always need to return a newly constructed object of our type + return new ModelSwaper(); +}); \ No newline at end of file From c2894749096321502d23b8dc7a986365e6873b86 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 6 Nov 2015 14:25:01 -0800 Subject: [PATCH 0748/1003] avatr mover --- examples/avatarMover/avatarMover.js | 188 +++++++++++++++++++++ examples/avatarMover/avatarMoverSpawner.js | 6 + 2 files changed, 194 insertions(+) create mode 100644 examples/avatarMover/avatarMover.js create mode 100644 examples/avatarMover/avatarMoverSpawner.js diff --git a/examples/avatarMover/avatarMover.js b/examples/avatarMover/avatarMover.js new file mode 100644 index 0000000000..a47bd01ed5 --- /dev/null +++ b/examples/avatarMover/avatarMover.js @@ -0,0 +1,188 @@ +(function() { + this.defaultRange = 5; + this.acceleration = { + x: 0, + y: 0, + z: 0 + }; + this.onColor = { + red: 77, + green: 11, + blue: 111 + }; + this.offColor = { + red: 200, + green: 0, + blue: 0 + }; + var self = this; + //Default forward direction of mover object + this.forward = { + x: 0, + y: 0, + z: -1 + }; + this.isMoving = false; + this.velocity = { + x: 0, + y: 0, + z: 0 + }; + this.defaultThrust = 500; + this.maxRotMixVal = 0.01; + this.minRotMixVal = this.maxRotMixVal * 0.5; + this.minThrustPercentage = 0.2; + this.userData = {}; + + + this.getUserData = function() { + if (this.properties.userData) { + this.userData = JSON.parse(this.properties.userData); + } + } + + this.updateUserData = function() { + Entities.editEntity(this.entityId, { + userData: JSON.stringify(this.userData) + }); + } + + + this.toggleMover = function() { + if (!this.userData.active) { + this.activate(); + } else if (this.userData.active) { + this.deactivate(); + } + + } + + this.clickReleaseOnEntity = function(entityId, mouseEvent) { + this.entityId = entityId + if (mouseEvent.isLeftButton) { + this.toggleMover(); + } + } + + this.activate = function() { + //activate a light at the movers position + this.properties = Entities.getEntityProperties(this.entityId); + this.getUserData(); + this.userData.active = true; + this.initUserData(); + var lightPos = this.properties.position; + lightPos.y += .1; + this.light = Entities.addEntity({ + type: "Light", + position: lightPos, + isSpotlight: false, + dimensions: { + x: 2, + y: 2, + z: 2 + }, + color: this.onColor, + intensity: 10 + // rotation: {x : 0, y: Math.PI/2, z: 0} + }); + + this.field = Overlays.addOverlay("sphere", { + position: this.properties.position, + size: this.userData.range, + solid: false, + color: { + red: 250, + green: 10, + blue: 10 + }, + }) + + //change color + Entities.editEntity(this.entityId, { + color: this.onColor, + }); + } + + this.initUserData = function() { + this.userData.range = this.userData.range || this.defaultRange; + this.userData.thrust = this.userData.thrust || this.defaultThrust; + this.updateUserData(); + } + + this.updateOverlays = function() { + if (this.field) { + Overlays.editOverlay(this.field, { + size: this.userData.range + }); + } + } + + + this.deactivate = function() { + this.userData.active = false; + this.updateUserData(); + Entities.editEntity(this.entityId, { + color: this.offColor + }); + this.cleanUp(); + } + + this.scriptEnding = function() { + this.cleanUp(); + } + + this.update = function(deltaTime) { + self.properties = Entities.getEntityProperties(self.entityId); + self.getUserData(); + self.updateOverlays(); + if (!self.userData.active) { + return; + } + self.distance = Vec3.distance(MyAvatar.position, self.properties.position); + if (self.distance < self.userData.range) { + self.rotationMixVal = map(self.distance, 0, self.userData.range, self.maxRotMixVal, self.minRotMixVal); + + //We want to extract yaw from rotated object so avatars do not pith or roll, as they will be stuck that way. + self.sanitizedRotation = Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(self.properties.rotation).y, 0); + self.newOrientation = Quat.mix(MyAvatar.orientation, self.sanitizedRotation, self.rotationMixVal); + MyAvatar.orientation = self.newOrientation; + + self.rotatedDir = { + x: self.forward.x, + y: self.forward.y, + z: self.forward.z + }; + self.rotatedDir = Vec3.multiplyQbyV(self.properties.rotation, self.rotatedDir); + + self.thrust = map(self.distance, 0, self.userData.range, self.userData.thrust, self.userData.thrust * self.minThrustPercentage); + self.direction = Vec3.normalize(self.rotatedDir); + self.velocity = Vec3.multiply(self.direction, self.thrust); + MyAvatar.addThrust(Vec3.multiply(self.velocity, deltaTime)); + } + + } + + + this.preload = function(entityId) { + this.entityId = entityId; + } + + this.unload = function() { + Script.update.disconnect(this.update); + this.cleanUp(); + } + + + this.cleanUp = function() { + Entities.deleteEntity(this.light); + Overlays.deleteOverlay(this.field); + } + + function map(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); + } + + Script.scriptEnding.connect(this.scriptEnding); + Script.update.connect(this.update); + +}); \ No newline at end of file diff --git a/examples/avatarMover/avatarMoverSpawner.js b/examples/avatarMover/avatarMoverSpawner.js new file mode 100644 index 0000000000..0bff3b02c0 --- /dev/null +++ b/examples/avatarMover/avatarMoverSpawner.js @@ -0,0 +1,6 @@ +var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/arrow.fbx"; + +var avatarMover = Entities.addEntity({ + type: "Model", + modelURL: modelURL +}) \ No newline at end of file From 4513b638dbec8e15149801d1b408fdcdccb2ee98 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 14:40:10 -0800 Subject: [PATCH 0749/1003] Adding clip serialization implementation, tests --- libraries/recording/src/recording/Clip.cpp | 27 +-- libraries/recording/src/recording/Clip.h | 19 +- libraries/recording/src/recording/Frame.h | 4 + libraries/recording/src/recording/Logging.cpp | 11 + libraries/recording/src/recording/Logging.h | 16 ++ .../recording/src/recording/Recorder.cpp | 2 +- .../src/recording/impl/BufferClip.cpp | 18 +- .../recording/src/recording/impl/BufferClip.h | 10 +- .../recording/src/recording/impl/FileClip.cpp | 196 ++++++++++++++++-- .../recording/src/recording/impl/FileClip.h | 29 ++- tests/recording/CMakeLists.txt | 22 +- tests/recording/src/Constants.h | 2 + tests/recording/src/FrameTests.cpp | 4 + tests/recording/src/FrameTests.h | 3 + tests/recording/src/RecorderTests.cpp | 4 + tests/recording/src/RecorderTests.h | 4 + tests/recording/src/main.cpp | 114 ++++++++++ 17 files changed, 427 insertions(+), 58 deletions(-) create mode 100644 libraries/recording/src/recording/Logging.cpp create mode 100644 libraries/recording/src/recording/Logging.h create mode 100644 tests/recording/src/main.cpp diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index ef59532f09..09acf0579f 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -16,33 +16,34 @@ using namespace recording; Clip::Pointer Clip::fromFile(const QString& filePath) { - return std::make_shared(filePath); + auto result = std::make_shared(filePath); + if (result->frameCount() == 0) { + return Clip::Pointer(); + } + return result; } -void Clip::toFile(Clip::Pointer clip, const QString& filePath) { - // FIXME +void Clip::toFile(const QString& filePath, Clip::Pointer clip) { + FileClip::write(filePath, clip->duplicate()); +} + +Clip::Pointer Clip::newClip() { + return std::make_shared(); } Clip::Pointer Clip::duplicate() { Clip::Pointer result = std::make_shared(); + Locker lock(_mutex); float currentPosition = position(); seek(0); Frame::Pointer frame = nextFrame(); while (frame) { - result->appendFrame(frame); + result->addFrame(frame); + frame = nextFrame(); } seek(currentPosition); return result; } - -#if 0 -Clip::Pointer Clip::fromIODevice(QIODevice * device) { - return std::make_shared(device); -} - -void Clip::fromIODevice(Clip::Pointer clip, QIODevice * device) { -} -#endif \ No newline at end of file diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index ca77ba8969..e7034ef077 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -12,35 +12,44 @@ #include "Forward.h" +#include + #include class QIODevice; namespace recording { -class Clip : public QObject { +class Clip { public: using Pointer = std::shared_ptr; - Clip(QObject* parent = nullptr) : QObject(parent) {} virtual ~Clip() {} Pointer duplicate(); + virtual float duration() const = 0; + virtual size_t frameCount() const = 0; + virtual void seek(float offset) = 0; virtual float position() const = 0; virtual FramePointer peekFrame() const = 0; virtual FramePointer nextFrame() = 0; virtual void skipFrame() = 0; - virtual void appendFrame(FramePointer) = 0; - + virtual void addFrame(FramePointer) = 0; static Pointer fromFile(const QString& filePath); - static void toFile(Pointer clip, const QString& filePath); + static void toFile(const QString& filePath, Pointer clip); + static Pointer newClip(); protected: + using Mutex = std::recursive_mutex; + using Locker = std::unique_lock; + virtual void reset() = 0; + + mutable Mutex _mutex; }; } diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index 2834637a6b..0fb95c4b2e 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -29,6 +29,10 @@ public: float timeOffset { 0 }; QByteArray data; + Frame() {} + Frame(FrameType type, float timeOffset, const QByteArray& data) + : type(type), timeOffset(timeOffset), data(data) {} + static FrameType registerFrameType(const QString& frameTypeName); static QMap getFrameTypes(); static QMap getFrameTypeNames(); diff --git a/libraries/recording/src/recording/Logging.cpp b/libraries/recording/src/recording/Logging.cpp new file mode 100644 index 0000000000..5673e6e175 --- /dev/null +++ b/libraries/recording/src/recording/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(recordingLog, "hifi.recording") diff --git a/libraries/recording/src/recording/Logging.h b/libraries/recording/src/recording/Logging.h new file mode 100644 index 0000000000..a1b28329d7 --- /dev/null +++ b/libraries/recording/src/recording/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(recordingLog) + +#endif diff --git a/libraries/recording/src/recording/Recorder.cpp b/libraries/recording/src/recording/Recorder.cpp index a38e4252b9..b2e7399cd4 100644 --- a/libraries/recording/src/recording/Recorder.cpp +++ b/libraries/recording/src/recording/Recorder.cpp @@ -51,7 +51,7 @@ void Recorder::recordFrame(FrameType type, QByteArray frameData) { frame->type = type; frame->data = frameData; frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND; - _clip->appendFrame(frame); + _clip->addFrame(frame); } ClipPointer Recorder::getClip() { diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index 378fee2558..4d5a910d42 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -51,11 +51,15 @@ FramePointer BufferClip::nextFrame() { return result; } -void BufferClip::appendFrame(FramePointer newFrame) { +void BufferClip::addFrame(FramePointer newFrame) { + if (newFrame->timeOffset < 0.0f) { + throw std::runtime_error("Frames may not have negative time offsets"); + } auto currentPosition = position(); seek(newFrame->timeOffset); { Locker lock(_mutex); + _frames.insert(_frames.begin() + _frameIndex, newFrame); } seek(currentPosition); @@ -72,3 +76,15 @@ void BufferClip::reset() { Locker lock(_mutex); _frameIndex = 0; } + +float BufferClip::duration() const { + if (_frames.empty()) { + return 0; + } + return (*_frames.rbegin())->timeOffset; +} + +size_t BufferClip::frameCount() const { + return _frames.size(); +} + diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index 8fd78a9091..b40687a4ec 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -20,25 +20,23 @@ class BufferClip : public Clip { public: using Pointer = std::shared_ptr; - BufferClip(QObject* parent = nullptr) : Clip(parent) {} virtual ~BufferClip() {} + virtual float duration() const override; + virtual size_t frameCount() const override; + virtual void seek(float offset) override; virtual float position() const override; virtual FramePointer peekFrame() const override; virtual FramePointer nextFrame() override; virtual void skipFrame() override; - virtual void appendFrame(FramePointer) override; + virtual void addFrame(FramePointer) override; private: - using Mutex = std::mutex; - using Locker = std::unique_lock; - virtual void reset() override; std::vector _frames; - mutable Mutex _mutex; mutable size_t _frameIndex { 0 }; }; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index 354a97c5db..be7230e3f8 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -8,42 +8,197 @@ #include "FileClip.h" -#include "../Frame.h" - #include +#include +#include +#include + +#include + +#include "../Frame.h" +#include "../Logging.h" + + using namespace recording; -static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t) + 1; +static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t); -FileClip::FileClip(const QString& fileName, QObject* parent) : Clip(parent), _file(fileName) { - auto size = _file.size(); - _map = _file.map(0, size, QFile::MapPrivateOption); +static const QString FRAME_TYPE_MAP = QStringLiteral("frameTypes"); - auto current = _map; +using FrameHeaderList = std::list; +using FrameTranslationMap = QMap; + +FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) { + FrameTranslationMap results; + auto headerObj = doc.object(); + if (headerObj.contains(FRAME_TYPE_MAP)) { + auto frameTypeObj = headerObj[FRAME_TYPE_MAP].toObject(); + auto currentFrameTypes = Frame::getFrameTypes(); + for (auto frameTypeName : frameTypeObj.keys()) { + qDebug() << frameTypeName; + if (!currentFrameTypes.contains(frameTypeName)) { + continue; + } + FrameType currentTypeEnum = currentFrameTypes[frameTypeName]; + FrameType storedTypeEnum = static_cast(frameTypeObj[frameTypeName].toInt()); + results[storedTypeEnum] = currentTypeEnum; + } + } + return results; +} + + +FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) { + using FrameHeader = FileClip::FrameHeader; + FrameHeaderList results; + auto current = start; auto end = current + size; // Read all the frame headers - while (end - current < MINIMUM_FRAME_SIZE) { + // FIXME move to Frame::readHeader? + while (end - current >= MINIMUM_FRAME_SIZE) { FrameHeader header; memcpy(&(header.type), current, sizeof(FrameType)); current += sizeof(FrameType); - memcpy(&(header.timeOffset), current, sizeof(FrameType)); + memcpy(&(header.timeOffset), current, sizeof(float)); current += sizeof(float); memcpy(&(header.size), current, sizeof(uint16_t)); current += sizeof(uint16_t); - header.fileOffset = current - _map; + header.fileOffset = current - start; if (end - current < header.size) { + current = end; break; } - - _frameHeaders.push_back(header); + current += header.size; + results.push_back(header); } + return results; +} + + +FileClip::FileClip(const QString& fileName) : _file(fileName) { + auto size = _file.size(); + bool opened = _file.open(QIODevice::ReadOnly); + if (!opened) { + qCWarning(recordingLog) << "Unable to open file " << fileName; + return; + } + _map = _file.map(0, size, QFile::MapPrivateOption); + if (!_map) { + qCWarning(recordingLog) << "Unable to map file " << fileName; + return; + } + + FrameHeaderList parsedFrameHeaders = parseFrameHeaders(_map, size); + + // Verify that at least one frame exists and that the first frame is a header + if (0 == parsedFrameHeaders.size()) { + qWarning() << "No frames found, invalid file"; + return; + } + + // Grab the file header + { + auto fileHeaderFrameHeader = *parsedFrameHeaders.begin(); + parsedFrameHeaders.pop_front(); + if (fileHeaderFrameHeader.type != Frame::TYPE_HEADER) { + qWarning() << "Missing header frame, invalid file"; + return; + } + + QByteArray fileHeaderData((char*)_map + fileHeaderFrameHeader.fileOffset, fileHeaderFrameHeader.size); + _fileHeader = QJsonDocument::fromBinaryData(fileHeaderData); + } + + // Find the type enum translation map and fix up the frame headers + { + FrameTranslationMap translationMap = parseTranslationMap(_fileHeader); + if (translationMap.empty()) { + qWarning() << "Header missing frame type map, invalid file"; + return; + } + + // Update the loaded headers with the frame data + _frameHeaders.reserve(parsedFrameHeaders.size()); + for (auto& frameHeader : parsedFrameHeaders) { + if (!translationMap.contains(frameHeader.type)) { + continue; + } + frameHeader.type = translationMap[frameHeader.type]; + _frameHeaders.push_back(frameHeader); + } + } +} + +// FIXME move to frame? +bool writeFrame(QIODevice& output, const Frame& frame) { + auto written = output.write((char*)&(frame.type), sizeof(FrameType)); + if (written != sizeof(FrameType)) { + return false; + } + written = output.write((char*)&(frame.timeOffset), sizeof(float)); + if (written != sizeof(float)) { + return false; + } + uint16_t dataSize = frame.data.size(); + written = output.write((char*)&dataSize, sizeof(uint16_t)); + if (written != sizeof(uint16_t)) { + return false; + } + if (dataSize != 0) { + written = output.write(frame.data); + if (written != dataSize) { + return false; + } + } + return true; +} + +bool FileClip::write(const QString& fileName, Clip::Pointer clip) { + qCDebug(recordingLog) << "Writing clip to file " << fileName; + + if (0 == clip->frameCount()) { + return false; + } + + QFile outputFile(fileName); + if (!outputFile.open(QFile::Truncate | QFile::WriteOnly)) { + return false; + } + + Finally closer([&] { outputFile.close(); }); + { + auto frameTypes = Frame::getFrameTypes(); + QJsonObject frameTypeObj; + for (const auto& frameTypeName : frameTypes.keys()) { + frameTypeObj[frameTypeName] = frameTypes[frameTypeName]; + } + + QJsonObject rootObject; + rootObject.insert(FRAME_TYPE_MAP, frameTypeObj); + QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData(); + if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }))) { + return false; + } + } + + clip->seek(0); + for (auto frame = clip->nextFrame(); frame; frame = clip->nextFrame()) { + if (!writeFrame(outputFile, *frame)) { + return false; + } + } + outputFile.close(); + return true; } FileClip::~FileClip() { Locker lock(_mutex); _file.unmap(_map); _map = nullptr; + if (_file.isOpen()) { + _file.close(); + } } void FileClip::seek(float offset) { @@ -72,7 +227,9 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const { const FrameHeader& header = _frameHeaders[frameIndex]; result->type = header.type; result->timeOffset = header.timeOffset; - result->data.insert(0, reinterpret_cast(_map)+header.fileOffset, header.size); + if (header.size) { + result->data.insert(0, reinterpret_cast(_map)+header.fileOffset, header.size); + } } return result; } @@ -99,7 +256,18 @@ void FileClip::reset() { _frameIndex = 0; } -void FileClip::appendFrame(FramePointer) { +void FileClip::addFrame(FramePointer) { throw std::runtime_error("File clips are read only"); } +float FileClip::duration() const { + if (_frameHeaders.empty()) { + return 0; + } + return _frameHeaders.rbegin()->timeOffset; +} + +size_t FileClip::frameCount() const { + return _frameHeaders.size(); +} + diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h index 9b13adc9ef..08eacd8337 100644 --- a/libraries/recording/src/recording/impl/FileClip.h +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -13,6 +13,7 @@ #include "../Clip.h" #include +#include #include @@ -22,22 +23,25 @@ class FileClip : public Clip { public: using Pointer = std::shared_ptr; - FileClip(const QString& file, QObject* parent = nullptr); + FileClip(const QString& file); virtual ~FileClip(); + virtual float duration() const override; + virtual size_t frameCount() const override; + virtual void seek(float offset) override; virtual float position() const override; virtual FramePointer peekFrame() const override; virtual FramePointer nextFrame() override; - virtual void appendFrame(FramePointer) override; virtual void skipFrame() override; + virtual void addFrame(FramePointer) override; -private: - using Mutex = std::mutex; - using Locker = std::unique_lock; + const QJsonDocument& getHeader() { + return _fileHeader; + } - virtual void reset() override; + static bool write(const QString& filePath, Clip::Pointer clip); struct FrameHeader { FrameType type; @@ -46,15 +50,20 @@ private: quint64 fileOffset; }; - using FrameHeaders = std::vector; +private: + + virtual void reset() override; + + + using FrameHeaderVector = std::vector; FramePointer readFrame(uint32_t frameIndex) const; - mutable Mutex _mutex; + QJsonDocument _fileHeader; QFile _file; uint32_t _frameIndex { 0 }; - uchar* _map; - FrameHeaders _frameHeaders; + uchar* _map { nullptr }; + FrameHeaderVector _frameHeaders; }; } diff --git a/tests/recording/CMakeLists.txt b/tests/recording/CMakeLists.txt index f1e130956d..a523947f52 100644 --- a/tests/recording/CMakeLists.txt +++ b/tests/recording/CMakeLists.txt @@ -1,10 +1,16 @@ +set(TARGET_NAME recording-test) +# This is not a testcase -- just set it up as a regular hifi project +setup_hifi_project(Test) +set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") +link_hifi_libraries(shared recording) +copy_dlls_beside_windows_executable() +# FIXME convert to unit tests # Declare dependencies -macro (setup_testcase_dependencies) - # link in the shared libraries - link_hifi_libraries(shared recording) - - copy_dlls_beside_windows_executable() -endmacro () - -setup_hifi_testcase() +#macro (setup_testcase_dependencies) +# # link in the shared libraries +# link_hifi_libraries(shared recording) +# +# copy_dlls_beside_windows_executable() +#endmacro () +#setup_hifi_testcase() diff --git a/tests/recording/src/Constants.h b/tests/recording/src/Constants.h index 37758bfd68..2aa19fe786 100644 --- a/tests/recording/src/Constants.h +++ b/tests/recording/src/Constants.h @@ -11,6 +11,8 @@ #ifndef hifi_Constants_h #define hifi_Constants_h +#include + static const QString HEADER_NAME = "com.highfidelity.recording.Header"; static const QString TEST_NAME = "com.highfidelity.recording.Test"; diff --git a/tests/recording/src/FrameTests.cpp b/tests/recording/src/FrameTests.cpp index f61d45c197..ebd7f90e31 100644 --- a/tests/recording/src/FrameTests.cpp +++ b/tests/recording/src/FrameTests.cpp @@ -8,6 +8,9 @@ #include "FrameTests.h" #include "Constants.h" + +#if 0 + #include "../QTestExtensions.h" #include @@ -27,3 +30,4 @@ void FrameTests::registerFrameTypeTest() { QCOMPARE(backMap[recording::Frame::TYPE_HEADER], HEADER_NAME); } +#endif diff --git a/tests/recording/src/FrameTests.h b/tests/recording/src/FrameTests.h index 04f73532a5..bdf4542846 100644 --- a/tests/recording/src/FrameTests.h +++ b/tests/recording/src/FrameTests.h @@ -10,6 +10,7 @@ #ifndef hifi_FrameTests_h #define hifi_FrameTests_h +#if 0 #include class FrameTests : public QObject { @@ -18,4 +19,6 @@ private slots: void registerFrameTypeTest(); }; +#endif + #endif // hifi_FrameTests_h diff --git a/tests/recording/src/RecorderTests.cpp b/tests/recording/src/RecorderTests.cpp index 76b4b46577..b102a3c931 100644 --- a/tests/recording/src/RecorderTests.cpp +++ b/tests/recording/src/RecorderTests.cpp @@ -8,6 +8,9 @@ #include "RecorderTests.h" #include "Constants.h" + +#if 0 + #include "../QTestExtensions.h" #include @@ -23,3 +26,4 @@ void RecorderTests::recorderTest() { //QCOMPARE(recoreder.isRecording(), false); } +#endif diff --git a/tests/recording/src/RecorderTests.h b/tests/recording/src/RecorderTests.h index 8e97a828a2..9bfd8e2d10 100644 --- a/tests/recording/src/RecorderTests.h +++ b/tests/recording/src/RecorderTests.h @@ -10,6 +10,8 @@ #ifndef hifi_RecorderTests_h #define hifi_RecorderTests_h +#if 0 + #include class RecorderTests : public QObject { @@ -19,3 +21,5 @@ private slots: }; #endif + +#endif diff --git a/tests/recording/src/main.cpp b/tests/recording/src/main.cpp new file mode 100644 index 0000000000..836d8b5ac1 --- /dev/null +++ b/tests/recording/src/main.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#ifdef Q_OS_WIN32 +#include +#endif + +#include +#include + +#include "Constants.h" + +#define QVERIFY Q_ASSERT + +using namespace recording; +FrameType TEST_FRAME_TYPE { Frame::TYPE_INVALID }; + +void testFrameTypeRegistration() { + TEST_FRAME_TYPE = Frame::registerFrameType(TEST_NAME); + QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_INVALID); + QVERIFY(TEST_FRAME_TYPE != Frame::TYPE_HEADER); + + auto forwardMap = recording::Frame::getFrameTypes(); + QVERIFY(forwardMap.count(TEST_NAME) == 1); + QVERIFY(forwardMap[TEST_NAME] == TEST_FRAME_TYPE); + QVERIFY(forwardMap[HEADER_NAME] == recording::Frame::TYPE_HEADER); + + auto backMap = recording::Frame::getFrameTypeNames(); + QVERIFY(backMap.count(TEST_FRAME_TYPE) == 1); + QVERIFY(backMap[TEST_FRAME_TYPE] == TEST_NAME); + QVERIFY(backMap[recording::Frame::TYPE_HEADER] == HEADER_NAME); +} + +void testFilePersist() { + QTemporaryFile file; + QString fileName; + if (file.open()) { + fileName = file.fileName(); + file.close(); + } + auto readClip = Clip::fromFile(fileName); + QVERIFY(Clip::Pointer() == readClip); + auto writeClip = Clip::newClip(); + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 5.0f, QByteArray())); + QVERIFY(writeClip->frameCount() == 1); + QVERIFY(writeClip->duration() == 5.0f); + + Clip::toFile(fileName, writeClip); + readClip = Clip::fromFile(fileName); + QVERIFY(readClip != Clip::Pointer()); + QVERIFY(readClip->frameCount() == 1); + QVERIFY(readClip->duration() == 5.0f); + readClip->seek(0); + writeClip->seek(0); + + size_t count = 0; + for (auto readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(); readFrame && writeFrame; + readFrame = readClip->nextFrame(), writeFrame = writeClip->nextFrame(), ++count) { + QVERIFY(readFrame->type == writeFrame->type); + QVERIFY(readFrame->timeOffset == writeFrame->timeOffset); + QVERIFY(readFrame->data == writeFrame->data); + } + QVERIFY(readClip->frameCount() == count); + + + writeClip = Clip::newClip(); + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 5.0f, QByteArray())); + // Simulate an unknown frametype + writeClip->addFrame(std::make_shared(Frame::TYPE_INVALID - 1, 10.0f, QByteArray())); + QVERIFY(writeClip->frameCount() == 2); + QVERIFY(writeClip->duration() == 10.0f); + Clip::toFile(fileName, writeClip); + + // Verify that the read version of the clip ignores the unknown frame type + readClip = Clip::fromFile(fileName); + QVERIFY(readClip != Clip::Pointer()); + QVERIFY(readClip->frameCount() == 1); + QVERIFY(readClip->duration() == 5.0f); +} + +void testClipOrdering() { + auto writeClip = Clip::newClip(); + // simulate our of order addition of frames + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 10.0f, QByteArray())); + writeClip->addFrame(std::make_shared(TEST_FRAME_TYPE, 5.0f, QByteArray())); + QVERIFY(writeClip->frameCount() == 2); + QVERIFY(writeClip->duration() == 10.0f); + + QVERIFY(std::numeric_limits::max() == writeClip->position()); + writeClip->seek(0); + QVERIFY(5.0f == writeClip->position()); + float lastFrameTimeOffset { 0 }; + for (auto writeFrame = writeClip->nextFrame(); writeFrame; writeFrame = writeClip->nextFrame()) { + QVERIFY(writeClip->position() >= lastFrameTimeOffset); + } +} + +#ifdef Q_OS_WIN32 +void myMessageHandler(QtMsgType type, const QMessageLogContext & context, const QString & msg) { + OutputDebugStringA(msg.toLocal8Bit().toStdString().c_str()); + OutputDebugStringA("\n"); +} +#endif + +int main(int, const char**) { +#ifdef Q_OS_WIN32 + qInstallMessageHandler(myMessageHandler); +#endif + testFrameTypeRegistration(); + testFilePersist(); + testClipOrdering(); +} \ No newline at end of file From c002888808765940fc0b4dd42d444f6be25645d5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 6 Nov 2015 14:52:12 -0800 Subject: [PATCH 0750/1003] Added cache extractor to the tools directory It should find the local High Fidelity/Interface qt cache, iterate over each file in the cache and output each corresponding file into the High Fidelity/Interface/extracted dir. The file path will be determined from the source url. Untested on windows. --- tools/CMakeLists.txt | 3 + tools/cache-extract/CMakeLists.txt | 6 + tools/cache-extract/src/CacheExtractApp.cpp | 125 ++++++++++++++++++++ tools/cache-extract/src/CacheExtractApp.h | 47 ++++++++ tools/cache-extract/src/main.cpp | 17 +++ 5 files changed, 198 insertions(+) create mode 100644 tools/cache-extract/CMakeLists.txt create mode 100644 tools/cache-extract/src/CacheExtractApp.cpp create mode 100644 tools/cache-extract/src/CacheExtractApp.h create mode 100644 tools/cache-extract/src/main.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 2056044a4b..9bc7031720 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -10,3 +10,6 @@ set_target_properties(udt-test PROPERTIES FOLDER "Tools") add_subdirectory(vhacd-util) set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") + +add_subdirectory(cache-extract) +set_target_properties(cache-extract PROPERTIES FOLDER "Tools") diff --git a/tools/cache-extract/CMakeLists.txt b/tools/cache-extract/CMakeLists.txt new file mode 100644 index 0000000000..045fa996d2 --- /dev/null +++ b/tools/cache-extract/CMakeLists.txt @@ -0,0 +1,6 @@ +set(TARGET_NAME cache-extract) + +setup_hifi_project() + +link_hifi_libraries() + diff --git a/tools/cache-extract/src/CacheExtractApp.cpp b/tools/cache-extract/src/CacheExtractApp.cpp new file mode 100644 index 0000000000..e7e60973af --- /dev/null +++ b/tools/cache-extract/src/CacheExtractApp.cpp @@ -0,0 +1,125 @@ +// +// CacheExtractApp.cpp +// tools/cache-extract/src +// +// Created by Anthony Thibault on 11/6/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 +// + +#include "CacheExtractApp.h" +#include +#include +#include +#include + +// extracted from qnetworkdiskcache.cpp +#define CACHE_VERSION 8 +enum { + CacheMagic = 0xe8, + CurrentCacheVersion = CACHE_VERSION +}; +#define DATA_DIR QLatin1String("data") + +CacheExtractApp::CacheExtractApp(int& argc, char** argv) : + QCoreApplication(argc, argv) +{ + QString myDataLoc = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + int lastSlash = myDataLoc.lastIndexOf(QDir::separator()); + QString cachePath = myDataLoc.leftRef(lastSlash).toString() + QDir::separator() + + "High Fidelity" + QDir::separator() + "Interface" + QDir::separator() + + DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/'); + + QString outputPath = myDataLoc.leftRef(lastSlash).toString() + QDir::separator() + + "High Fidelity" + QDir::separator() + "Interface" + QDir::separator() + "extracted"; + + qDebug() << "Searching cachePath = " << cachePath << "..."; + + // build list of files + QList fileList; + QDir dir(cachePath); + dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); + QFileInfoList list = dir.entryInfoList(); + for (int i = 0; i < list.size(); ++i) { + QFileInfo fileInfo = list.at(i); + if (fileInfo.isDir()) { + QDir subDir(fileInfo.filePath()); + subDir.setFilter(QDir::Files); + QFileInfoList subList = subDir.entryInfoList(); + for (int j = 0; j < subList.size(); ++j) { + fileList << subList.at(j).filePath(); + } + } + } + + // dump each cache file into the outputPath + for (int i = 0; i < fileList.size(); ++i) { + QByteArray contents; + MyMetaData metaData; + if (extractFile(fileList.at(i), metaData, contents)) { + QString outFileName = outputPath + metaData.url.path(); + int lastSlash = outFileName.lastIndexOf(QDir::separator()); + QString outDirName = outFileName.leftRef(lastSlash).toString(); + QDir dir(outputPath); + dir.mkpath(outDirName); + QFile out(outFileName); + if (out.open(QIODevice::WriteOnly)) { + out.write(contents); + out.close(); + } + } else { + qCritical() << "Error extracting = " << fileList.at(i); + } + } + + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); +} + +bool CacheExtractApp::extractFile(const QString& filePath, MyMetaData& metaData, QByteArray& data) const { + QFile f(filePath); + if (!f.open(QIODevice::ReadOnly)) { + qDebug() << "error opening " << filePath; + return false; + } + QDataStream in(&f); + // from qnetworkdiskcache.cpp QCacheItem::read() + qint32 marker, version, streamVersion; + in >> marker; + if (marker != CacheMagic) { + return false; + } + in >> version; + if (version != CurrentCacheVersion) { + return false; + } + in >> streamVersion; + if (streamVersion > in.version()) + return false; + in.setVersion(streamVersion); + + bool compressed; + in >> metaData; + in >> compressed; + if (compressed) { + QByteArray compressedData; + in >> compressedData; + QBuffer buffer; + buffer.setData(qUncompress(compressedData)); + buffer.open(QBuffer::ReadOnly); + data = buffer.readAll(); + } else { + data = f.readAll(); + } + return true; +} + +QDataStream &operator>>(QDataStream& in, MyMetaData& metaData) { + in >> metaData.url; + in >> metaData.expirationDate; + in >> metaData.lastModified; + in >> metaData.saveToDisk; + in >> metaData.attributes; + in >> metaData.rawHeaders; +} diff --git a/tools/cache-extract/src/CacheExtractApp.h b/tools/cache-extract/src/CacheExtractApp.h new file mode 100644 index 0000000000..3dde1f684d --- /dev/null +++ b/tools/cache-extract/src/CacheExtractApp.h @@ -0,0 +1,47 @@ +// +// CacheExtractApp.h +// tools/cache-extract/src +// +// Created by Anthony Thibault on 11/6/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 +// + +#ifndef hifi_CacheExtractApp_h +#define hifi_CacheExtractApp_h + +#include +#include +#include +#include +#include +#include + +// copy of QNetworkCacheMetaData +class MyMetaData { +public: + typedef QPair RawHeader; + typedef QList RawHeaderList; + typedef QHash AttributesMap; + + QUrl url; + QDateTime expirationDate; + QDateTime lastModified; + bool saveToDisk; + AttributesMap attributes; + RawHeaderList rawHeaders; +}; + +QDataStream &operator>>(QDataStream &, MyMetaData &); + +class CacheExtractApp : public QCoreApplication { + Q_OBJECT +public: + CacheExtractApp(int& argc, char** argv); + + bool extractFile(const QString& filePath, MyMetaData& metaData, QByteArray& data) const; +}; + +#endif // hifi_CacheExtractApp_h diff --git a/tools/cache-extract/src/main.cpp b/tools/cache-extract/src/main.cpp new file mode 100644 index 0000000000..71a364ed3e --- /dev/null +++ b/tools/cache-extract/src/main.cpp @@ -0,0 +1,17 @@ +// +// main.cpp +// tools/cache-extract/src +// +// Created by Anthony Thibault on 11/6/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 + +#include +#include "CacheExtractApp.h" + +int main (int argc, char** argv) { + CacheExtractApp app(argc, argv); + return app.exec(); +} From 086b0645273f33c9fc00cfd99cb82b82fef0725e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 6 Nov 2015 15:07:38 -0800 Subject: [PATCH 0751/1003] Dumps all urls extracted to stdout. --- tools/cache-extract/src/CacheExtractApp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cache-extract/src/CacheExtractApp.cpp b/tools/cache-extract/src/CacheExtractApp.cpp index e7e60973af..a0d0bf54ec 100644 --- a/tools/cache-extract/src/CacheExtractApp.cpp +++ b/tools/cache-extract/src/CacheExtractApp.cpp @@ -68,6 +68,7 @@ CacheExtractApp::CacheExtractApp(int& argc, char** argv) : if (out.open(QIODevice::WriteOnly)) { out.write(contents); out.close(); + qDebug().noquote() << metaData.url.toDisplayString(); } } else { qCritical() << "Error extracting = " << fileList.at(i); From ced1a4899de4edc4edd1f7793bcc7c0b457ebd5f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 15:10:23 -0800 Subject: [PATCH 0752/1003] promises --- examples/libraries/promise.js | 222 +++++++++++++++++++++++++++ examples/libraries/promiseExample.js | 18 +++ 2 files changed, 240 insertions(+) create mode 100644 examples/libraries/promise.js create mode 100644 examples/libraries/promiseExample.js diff --git a/examples/libraries/promise.js b/examples/libraries/promise.js new file mode 100644 index 0000000000..cffa294715 --- /dev/null +++ b/examples/libraries/promise.js @@ -0,0 +1,222 @@ +// Copyright (c) 2014 Taylor Hakes +// Copyright (c) 2014 Forbes Lindesay + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + + +function promiseMaker() { + + // Use polyfill for setImmediate for performance gains + var asap = (typeof setImmediate === 'function' && setImmediate) || + function(fn) { + Script.setTimeout(fn, 1); + }; + + // Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function() { + fn.apply(thisArg, arguments); + } + } + + var isArray = Array.isArray || function(value) { + return Object.prototype.toString.call(value) === "[object Array]" + }; + + function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + this._state = null; + this._value = null; + this._deferreds = [] + + doResolve(fn, bind(resolve, this), bind(reject, this)) + } + + function handle(deferred) { + var me = this; + if (this._state === null) { + this._deferreds.push(deferred); + return + } + asap(function() { + var cb = me._state ? deferred.onFulfilled : deferred.onRejected + if (cb === null) { + (me._state ? deferred.resolve : deferred.reject)(me._value); + return; + } + var ret; + try { + ret = cb(me._value); + } catch (e) { + deferred.reject(e); + return; + } + deferred.resolve(ret); + }) + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.'); + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then; + if (typeof then === 'function') { + doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this)); + return; + } + } + this._state = true; + this._value = newValue; + finale.call(this); + } catch (e) { + reject.call(this, e); + } + } + + function reject(newValue) { + this._state = false; + this._value = newValue; + finale.call(this); + } + + function finale() { + for (var i = 0, len = this._deferreds.length; i < len; i++) { + handle.call(this, this._deferreds[i]); + } + this._deferreds = null; + } + + function Handler(onFulfilled, onRejected, resolve, reject) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.resolve = resolve; + this.reject = reject; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function(value) { + if (done) return; + done = true; + onFulfilled(value); + }, function(reason) { + if (done) return; + done = true; + onRejected(reason); + }) + } catch (ex) { + if (done) return; + done = true; + onRejected(ex); + } + } + + Promise.prototype['catch'] = function(onRejected) { + return this.then(null, onRejected); + }; + + Promise.prototype.then = function(onFulfilled, onRejected) { + var me = this; + return new Promise(function(resolve, reject) { + handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject)); + }) + }; + + Promise.all = function() { + var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments); + + return new Promise(function(resolve, reject) { + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call(val, function(val) { + res(i, val) + }, reject); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise.resolve = function(value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function(resolve) { + resolve(value); + }); + }; + + Promise.reject = function(value) { + return new Promise(function(resolve, reject) { + reject(value); + }); + }; + + Promise.race = function(values) { + return new Promise(function(resolve, reject) { + for (var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); + }; + + /** + * Set the immediate function to execute callbacks + * @param fn {function} Function to execute + * @private + */ + Promise._setImmediateFn = function _setImmediateFn(fn) { + asap = fn; + }; + + + return Promise + +} + +loadPromise = function() { + return promiseMaker(); +} \ No newline at end of file diff --git a/examples/libraries/promiseExample.js b/examples/libraries/promiseExample.js new file mode 100644 index 0000000000..817a78d2b0 --- /dev/null +++ b/examples/libraries/promiseExample.js @@ -0,0 +1,18 @@ +Script.include('promise.js'); +var Promise = loadPromise(); +var prom = new Promise(function(resolve, reject) { + print('making a promise') + // do a thing, possibly async, then… + var thing = true; + if (thing) { + resolve("Stuff worked!"); + } else { + print('ERROR') + reject(new Error("It broke")); + } +}); + +// Do something when async done +prom.then(function(result) { + print('result ' + result); +}); \ No newline at end of file From c1690bcf322d24bdc200e86201db6f4ef44db20c Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Fri, 6 Nov 2015 15:19:14 -0800 Subject: [PATCH 0753/1003] restoring avatarMover app post S3-loss event --- examples/avatarMover/avatarMoverSpawner.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/examples/avatarMover/avatarMoverSpawner.js b/examples/avatarMover/avatarMoverSpawner.js index 0bff3b02c0..526446ecdb 100644 --- a/examples/avatarMover/avatarMoverSpawner.js +++ b/examples/avatarMover/avatarMoverSpawner.js @@ -1,6 +1,18 @@ var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/arrow.fbx"; +var scriptURL = Script.resolvePath('avatarMover.js'); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); var avatarMover = Entities.addEntity({ type: "Model", - modelURL: modelURL -}) \ No newline at end of file + modelURL: modelURL, + position: center, + userData: JSON.stringify({range: 5}), + script: scriptURL +}); + + +function cleanup() { + Entities.deleteEntity(avatarMover); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 7c433f47c01956a2c424e538d08478abe27a10e9 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 3 Nov 2015 17:39:21 -0800 Subject: [PATCH 0754/1003] Breaking up input devices and input plugins classes. --- interface/src/Application.cpp | 8 +- interface/src/Menu.cpp | 2 - .../openvr/OpenVrDisplayPlugin.cpp | 25 ++--- .../openvr/OpenVrDisplayPlugin.h | 2 + .../display-plugins/openvr/OpenVrHelpers.cpp | 73 +++++++++++++ .../display-plugins/openvr/OpenVrHelpers.h | 6 + .../src/input-plugins/KeyboardMouseDevice.cpp | 74 +++++++------ .../src/input-plugins/KeyboardMouseDevice.h | 49 ++++++--- .../src/input-plugins/SixenseManager.cpp | 101 +++++++++-------- .../src/input-plugins/SixenseManager.h | 91 +++++++++------- .../src/input-plugins/SpacemouseManager.cpp | 15 +-- .../src/input-plugins/SpacemouseManager.h | 3 - .../input-plugins/ViveControllerManager.cpp | 103 +++++++----------- .../src/input-plugins/ViveControllerManager.h | 54 +++++---- tests/controllers/src/main.cpp | 2 +- 15 files changed, 346 insertions(+), 262 deletions(-) create mode 100644 libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8bff0f87dc..7a564bbbf0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -653,7 +653,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : userInputMapper->registerDevice(_applicationStateDevice); // Setup the keyboardMouseDevice and the user input mapper with the default bindings - userInputMapper->registerDevice(_keyboardMouseDevice); + userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); @@ -729,8 +729,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Now that menu is initalized we can sync myAvatar with it's state. getMyAvatar()->updateMotionBehaviorFromMenu(); +// FIXME spacemouse code still needs cleanup +#if 0 // the 3Dconnexion device wants to be initiliazed after a window is displayed. SpacemouseManager::getInstance().init(); +#endif auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); @@ -1850,9 +1853,12 @@ void Application::focusOutEvent(QFocusEvent* event) { } } +// FIXME spacemouse code still needs cleanup +#if 0 //SpacemouseDevice::getInstance().focusOutEvent(); //SpacemouseManager::getInstance().getDevice()->focusOutEvent(); SpacemouseManager::getInstance().ManagerFocusOutEvent(); +#endif // synthesize events for keys currently pressed, since we may not get their release events foreach (int key, _keysPressed) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 162b713948..92ff39a489 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -465,8 +465,6 @@ Menu::Menu() { avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::Connexion, 0, false, &SpacemouseManager::getInstance(), SLOT(toggleSpacemouse(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp index 174bf1bf36..bb39c7bb7a 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.cpp @@ -37,8 +37,6 @@ const QString & OpenVrDisplayPlugin::getName() const { return NAME; } -vr::IVRSystem* _hmd{ nullptr }; -int hmdRefCount = 0; static vr::IVRCompositor* _compositor{ nullptr }; vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; @@ -78,24 +76,17 @@ mat4 toGlm(const vr::HmdMatrix34_t& m) { } bool OpenVrDisplayPlugin::isSupported() const { - bool success = vr::VR_IsHmdPresent(); - if (success) { - vr::HmdError eError = vr::HmdError_None; - auto hmd = vr::VR_Init(&eError); - success = (hmd != nullptr); - vr::VR_Shutdown(); - } + auto hmd = acquireOpenVrSystem(); + bool success = nullptr != hmd; + releaseOpenVrSystem(); return success; } void OpenVrDisplayPlugin::activate() { _container->setIsOptionChecked(StandingHMDSensorMode, true); - hmdRefCount++; - vr::HmdError eError = vr::HmdError_None; if (!_hmd) { - _hmd = vr::VR_Init(&eError); - Q_ASSERT(eError == vr::HmdError_None); + _hmd = acquireOpenVrSystem(); } Q_ASSERT(_hmd); @@ -114,6 +105,7 @@ void OpenVrDisplayPlugin::activate() { }); + vr::HmdError eError = vr::HmdError_None; _compositor = (vr::IVRCompositor*)vr::VR_GetGenericInterface(vr::IVRCompositor_Version, &eError); Q_ASSERT(eError == vr::HmdError_None); Q_ASSERT(_compositor); @@ -133,11 +125,8 @@ void OpenVrDisplayPlugin::activate() { void OpenVrDisplayPlugin::deactivate() { _container->setIsOptionChecked(StandingHMDSensorMode, false); - - hmdRefCount--; - - if (hmdRefCount == 0 && _hmd) { - vr::VR_Shutdown(); + if (_hmd) { + releaseOpenVrSystem(); _hmd = nullptr; } _compositor = nullptr; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h index 7849623552..15d37d9de8 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrDisplayPlugin.h @@ -10,6 +10,7 @@ #include #if defined(Q_OS_WIN) +#include #include "../WindowOpenGLDisplayPlugin.h" @@ -39,6 +40,7 @@ protected: virtual void finishFrame() override; private: + vr::IVRSystem* _hmd { nullptr }; static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp new file mode 100644 index 0000000000..44d30962bd --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp @@ -0,0 +1,73 @@ +// +// Created by Bradley Austin Davis on 2015/11/01 +// 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 "OpenVrHelpers.h" + +#if defined(Q_OS_WIN) + +#include + +#include +#include + +using Mutex = std::mutex; +using Lock = std::unique_lock; + +static int refCount { 0 }; +static Mutex mutex; +static vr::IVRSystem* activeHmd { nullptr }; +static bool hmdPresent = vr::VR_IsHmdPresent(); + +static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; + +vr::IVRSystem* acquireOpenVrSystem() { + if (hmdPresent) { + Lock lock(mutex); + if (!activeHmd) { + qDebug() << "openvr: No vr::IVRSystem instance active, building"; + vr::HmdError eError = vr::HmdError_None; + activeHmd = vr::VR_Init(&eError); + qDebug() << "openvr display: HMD is " << activeHmd << " error is " << eError; + } + if (activeHmd) { + qDebug() << "openvr: incrementing refcount"; + ++refCount; + } + } + return activeHmd; +} + +void releaseOpenVrSystem() { + if (activeHmd) { + Lock lock(mutex); + qDebug() << "openvr: decrementing refcount"; + --refCount; + if (0 == refCount) { + qDebug() << "openvr: zero refcount, deallocate VR system"; + // Avoid spamming the VR system with activate/deactivate calls at system startup by + // putting in a delay before we destory the shutdown the VR subsystem + + // FIXME releasing the VR system at all seems to trigger an exception deep inside the Oculus DLL. + // disabling for now. + //QTimer* releaseTimer = new QTimer(); + //releaseTimer->singleShot(RELEASE_OPENVR_HMD_DELAY_MS, [releaseTimer] { + // Lock lock(mutex); + // qDebug() << "Delayed openvr destroy activated"; + // if (0 == refCount && nullptr != activeHmd) { + // qDebug() << "Delayed openvr destroy: releasing resources"; + // activeHmd = nullptr; + // vr::VR_Shutdown(); + // } else { + // qDebug() << "Delayed openvr destroy: HMD still in use"; + // } + // releaseTimer->deleteLater(); + //}); + } + } +} + +#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h index 761bef8cfc..3e445d90ba 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.h @@ -7,10 +7,16 @@ // #pragma once +#include + #if defined(Q_OS_WIN) #include #include #include #include + +vr::IVRSystem* acquireOpenVrSystem(); +void releaseOpenVrSystem(); + #endif diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 2157a3a010..e91ea90aaf 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -19,8 +19,8 @@ const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; -void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { - _axisStateMap.clear(); +void KeyboardMouseDevice::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); // For touch event, we need to check that the last event is not too long ago // Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly @@ -35,26 +35,30 @@ void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { } } -void KeyboardMouseDevice::focusOutEvent() { +void KeyboardMouseDevice::InputDevice::update(float deltaTime, bool jointsCaptured) { + _axisStateMap.clear(); +} + +void KeyboardMouseDevice::InputDevice::focusOutEvent() { _buttonPressedMap.clear(); -}; +} void KeyboardMouseDevice::keyPressEvent(QKeyEvent* event) { - auto input = makeInput((Qt::Key) event->key()); - auto result = _buttonPressedMap.insert(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::Key) event->key()); + auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel()); if (!result.second) { // key pressed again ? without catching the release event ? } } void KeyboardMouseDevice::keyReleaseEvent(QKeyEvent* event) { - auto input = makeInput((Qt::Key) event->key()); - _buttonPressedMap.erase(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::Key) event->key()); + _inputDevice->_buttonPressedMap.erase(input.getChannel()); } void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { - auto input = makeInput((Qt::MouseButton) event->button()); - auto result = _buttonPressedMap.insert(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); + auto result = _inputDevice->_buttonPressedMap.insert(input.getChannel()); if (!result.second) { // key pressed again ? without catching the release event ? } @@ -65,32 +69,32 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic } void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { - auto input = makeInput((Qt::MouseButton) event->button()); - _buttonPressedMap.erase(input.getChannel()); + auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); + _inputDevice->_buttonPressedMap.erase(input.getChannel()); // if we pressed and released at the same location, then create a "_CLICKED" input for this button // we might want to add some small tolerance to this so if you do a small drag it still counts as // a clicked. if (_mousePressAt == event->pos()) { - _buttonPressedMap.insert(makeInput((Qt::MouseButton) event->button(), true).getChannel()); + _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } void KeyboardMouseDevice::eraseMouseClicked() { - _buttonPressedMap.erase(makeInput(Qt::LeftButton, true).getChannel()); - _buttonPressedMap.erase(makeInput(Qt::MiddleButton, true).getChannel()); - _buttonPressedMap.erase(makeInput(Qt::RightButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::LeftButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::MiddleButton, true).getChannel()); + _inputDevice->_buttonPressedMap.erase(_inputDevice->makeInput(Qt::RightButton, true).getChannel()); } void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { QPoint currentPos = event->pos(); QPoint currentMove = currentPos - _lastCursor; - _axisStateMap[makeInput(MOUSE_AXIS_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_POS] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_X_NEG] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); // Y mouse is inverted positive is pointing up the screen - _axisStateMap[makeInput(MOUSE_AXIS_Y_POS).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_Y_NEG).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_POS] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _lastCursor = currentPos; @@ -100,10 +104,10 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { auto currentMove = event->angleDelta() / 120.0f; - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); - _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_POS).getChannel()] = (currentMove.x() > 0 ? currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_X_NEG).getChannel()] = (currentMove.x() < 0 ? -currentMove.x() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_POS).getChannel()] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); } glm::vec2 evalAverageTouchPoints(const QList& points) { @@ -138,17 +142,17 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { } else { auto currentMove = currentPos - _lastTouch; - _axisStateMap[makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); - _axisStateMap[makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_POS).getChannel()] = (currentMove.x > 0 ? currentMove.x : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_X_NEG).getChannel()] = (currentMove.x < 0 ? -currentMove.x : 0.0f); // Y mouse is inverted positive is pointing up the screen - _axisStateMap[makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); - _axisStateMap[makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_POS).getChannel()] = (currentMove.y < 0 ? -currentMove.y : 0.0f); + _inputDevice->_axisStateMap[_inputDevice->makeInput(TOUCH_AXIS_Y_NEG).getChannel()] = (currentMove.y > 0 ? currentMove.y : 0.0f); } _lastTouch = currentPos; } -controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { +controller::Input KeyboardMouseDevice::InputDevice::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 @@ -156,7 +160,7 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); } -controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clicked) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(Qt::MouseButton code, bool clicked) const { switch (code) { case Qt::LeftButton: return controller::Input(_deviceID, clicked ? MOUSE_BUTTON_LEFT_CLICKED : @@ -172,19 +176,19 @@ controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code, bool clic }; } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); } -controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { +controller::Input KeyboardMouseDevice::InputDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); } -controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { +controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInputs() const { using namespace controller; static QVector availableInputs; static std::once_flag once; @@ -229,7 +233,7 @@ controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { return availableInputs; } -QString KeyboardMouseDevice::getDefaultMappingConfig() const { +QString KeyboardMouseDevice::InputDevice::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 5d86821db1..b0578e3a99 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 controller::InputDevice { +class KeyboardMouseDevice : public InputPlugin { Q_OBJECT public: enum KeyboardChannel { @@ -64,22 +64,20 @@ public: TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, }; - KeyboardMouseDevice() : InputDevice("Keyboard") {} + KeyboardMouseDevice() {} + + virtual ~KeyboardMouseDevice() {} + + controller::InputDevice::Pointer getInputDevice() { return _inputDevice; } // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } const QString& getName() const override { return NAME; } - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; - void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); @@ -94,21 +92,36 @@ 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) const; - controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; - controller::Input makeInput(MouseAxisChannel axis) const; - controller::Input makeInput(TouchAxisChannel axis) const; - controller::Input makeInput(TouchButtonChannel button) const; - static const QString NAME; protected: + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Keyboard") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + // Let's make it easy for Qt because we assume we love Qt forever + controller::Input makeInput(Qt::Key code) const; + controller::Input makeInput(Qt::MouseButton code, bool clicked = false) const; + controller::Input makeInput(MouseAxisChannel axis) const; + controller::Input makeInput(TouchAxisChannel axis) const; + controller::Input makeInput(TouchButtonChannel button) const; + + friend class KeyboardMouseDevice; + }; + QPoint _lastCursor; QPoint _mousePressAt; glm::vec2 _lastTouch; + std::shared_ptr _inputDevice { std::make_shared() }; + bool _isTouching = false; - std::chrono::high_resolution_clock _clock; std::chrono::high_resolution_clock::time_point _lastTouchTime; }; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 024eb86182..33b4332430 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -30,6 +30,11 @@ #ifdef HAVE_SIXENSE #include "sixense.h" + +#ifdef __APPLE__ +static QLibrary* _sixenseLibrary { nullptr }; +#endif + #endif // TODO: This should not be here @@ -39,13 +44,10 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") #ifdef HAVE_SIXENSE -const int CALIBRATION_STATE_IDLE = 0; -const int CALIBRATION_STATE_IN_PROGRESS = 1; -const int CALIBRATION_STATE_COMPLETE = 2; -const glm::vec3 DEFAULT_AVATAR_POSITION(-0.25f, -0.35f, -0.3f); // in hydra frame - -const float CONTROLLER_THRESHOLD = 0.35f; +const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame +const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; +const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; #endif @@ -66,14 +68,6 @@ 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 { #ifdef HAVE_SIXENSE @@ -91,8 +85,6 @@ bool SixenseManager::isSupported() const { void SixenseManager::activate() { InputPlugin::activate(); #ifdef HAVE_SIXENSE - _calibrationState = CALIBRATION_STATE_IDLE; - _avatarPosition = DEFAULT_AVATAR_POSITION; _container->addMenu(MENU_PATH); _container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, @@ -100,7 +92,7 @@ void SixenseManager::activate() { true, true); auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); + userInputMapper->registerDevice(_inputDevice); #ifdef __APPLE__ @@ -139,12 +131,12 @@ void SixenseManager::deactivate() { _container->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); _container->removeMenu(MENU_PATH); - _poseStateMap.clear(); - _collectedSamples.clear(); + _inputDevice->_poseStateMap.clear(); + _inputDevice->_collectedSamples.clear(); - if (_deviceID != controller::Input::INVALID_DEVICE) { + if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(_deviceID); + userInputMapper->removeDevice(_inputDevice->_deviceID); } #ifdef __APPLE__ @@ -170,7 +162,15 @@ void SixenseManager::setSixenseFilter(bool filter) { #endif } -void SixenseManager::update(float deltaTime, bool jointsCaptured) { +void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); + if (_inputDevice->_calibrationState == CALIBRATION_STATE_COMPLETE) { + _container->requestReset(); + _inputDevice->_calibrationState = CALIBRATION_STATE_IDLE; + } +} + +void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // 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. @@ -297,7 +297,7 @@ const float MINIMUM_ARM_REACH = 0.3f; // meters const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired -void SixenseManager::updateCalibration(void* controllersX) { +void SixenseManager::InputDevice::updateCalibration(void* controllersX) { auto controllers = reinterpret_cast(controllersX); const sixenseControllerData* dataLeft = controllers; const sixenseControllerData* dataRight = controllers + 1; @@ -309,26 +309,25 @@ void SixenseManager::updateCalibration(void* controllersX) { } switch (_calibrationState) { case CALIBRATION_STATE_COMPLETE: - { - // compute calibration results - _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands - glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); - glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); - xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); - _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); - _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); - const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; - _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; - _container->requestReset(); - qCDebug(inputplugins, "succeess: sixense calibration"); - } - break; + { + // compute calibration results + _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands + glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); + glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); + xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); + _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); + _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); + const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; + _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; + qCDebug(inputplugins, "succeess: sixense calibration"); + } + break; + default: + _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); break; } - - _calibrationState = CALIBRATION_STATE_IDLE; return; } @@ -382,15 +381,15 @@ void SixenseManager::updateCalibration(void* controllersX) { #endif // HAVE_SIXENSE -void SixenseManager::focusOutEvent() { +void SixenseManager::InputDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; -void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { +void SixenseManager::InputDevice::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { } -void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { +void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { using namespace controller; if (buttons & BUTTON_0) { _buttonPressedMap.insert(left ? BACK : START); @@ -415,7 +414,7 @@ void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { } } -void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { +void SixenseManager::InputDevice::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; @@ -521,7 +520,7 @@ static const auto R4 = controller::Y; using namespace controller; -controller::Input::NamedVector SixenseManager::getAvailableInputs() const { +controller::Input::NamedVector SixenseManager::InputDevice::getAvailableInputs() const { using namespace controller; static const Input::NamedVector availableInputs { makePair(L0, "L0"), @@ -551,7 +550,7 @@ controller::Input::NamedVector SixenseManager::getAvailableInputs() const { }; -QString SixenseManager::getDefaultMappingConfig() const { +QString SixenseManager::InputDevice::getDefaultMappingConfig() const { static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json"; return MAPPING_JSON; } @@ -562,9 +561,9 @@ void SixenseManager::saveSettings() const { QString idString = getID(); settings.beginGroup(idString); { - settings.setVec3Value(QString("avatarPosition"), _avatarPosition); - settings.setQuatValue(QString("avatarRotation"), _avatarRotation); - settings.setValue(QString("reachLength"), QVariant(_reachLength)); + settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition); + settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.setValue(QString("reachLength"), QVariant(_inputDevice->_reachLength)); } settings.endGroup(); } @@ -574,9 +573,9 @@ void SixenseManager::loadSettings() { QString idString = getID(); settings.beginGroup(idString); { - settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); - settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); - settings.getFloatValueIfValid(QString("reachLength"), _reachLength); + settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition); + settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation); + settings.getFloatValueIfValid(QString("reachLength"), _inputDevice->_reachLength); } settings.endGroup(); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 5b5cb7ccfa..7a686dc423 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -44,10 +44,11 @@ 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 controller::InputDevice { +class SixenseManager : public InputPlugin { Q_OBJECT public: - SixenseManager(); + SixenseManager() {} + virtual ~SixenseManager() {} // Plugin functions virtual bool isSupported() const override; @@ -58,15 +59,8 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } - - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; virtual void saveSettings() const override; virtual void loadSettings() override; @@ -75,38 +69,57 @@ public slots: void setSixenseFilter(bool filter); private: - void handleButtonEvent(unsigned int buttons, bool left); - void handleAxisEvent(float x, float y, float trigger, bool left); - void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - - void updateCalibration(void* controllers); - - int _calibrationState; - - // these are calibration results - glm::vec3 _avatarPosition; // in hydra-frame - glm::quat _avatarRotation; // in hydra-frame - float _reachLength; - - // these are measured values used to compute the calibration results - quint64 _lockExpiry; - glm::vec3 _averageLeft; - glm::vec3 _averageRight; - glm::vec3 _reachLeft; - glm::vec3 _reachRight; - float _lastDistance; - bool _useSixenseFilter = true; - - static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s + static const int CALIBRATION_STATE_IDLE = 0; + static const int CALIBRATION_STATE_IN_PROGRESS = 1; + static const int CALIBRATION_STATE_COMPLETE = 2; + static const glm::vec3 DEFAULT_AVATAR_POSITION; + static const float CONTROLLER_THRESHOLD; + static const float DEFAULT_REACH_LENGTH; + + 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__ - QLibrary* _sixenseLibrary { nullptr }; -#endif + + class InputDevice : public controller::InputDevice { + public: + InputDevice() : controller::InputDevice("Hydra") {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + void handleButtonEvent(unsigned int buttons, bool left); + void handleAxisEvent(float x, float y, float trigger, bool left); + void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); + void updateCalibration(void* controllers); + + friend class SixenseManager; + + MovingAverageMap _collectedSamples; + + int _calibrationState { CALIBRATION_STATE_IDLE }; + // these are calibration results + glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame + glm::quat _avatarRotation; // in hydra-frame + float _reachLength { DEFAULT_REACH_LENGTH }; + float _lastDistance; + // these are measured values used to compute the calibration results + quint64 _lockExpiry; + glm::vec3 _averageLeft; + glm::vec3 _averageRight; + glm::vec3 _reachLeft; + glm::vec3 _reachRight; + }; + + + + bool _useSixenseFilter = true; + std::shared_ptr _inputDevice { std::make_shared() }; + static const QString NAME; static const QString HYDRA_ID_STRING; }; diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp index 43e6ee48a8..fe90470cb4 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp @@ -21,11 +21,10 @@ const float MAX_AXIS = 75.0f; // max forward = 2x speed -static std::shared_ptr instance; -SpacemouseDevice::SpacemouseDevice() : -InputDevice("Spacemouse") +static std::shared_ptr instance = std::make_shared(); + +SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") { - instance = std::shared_ptr(this); } void SpacemouseDevice::focusOutEvent() { @@ -118,14 +117,6 @@ void SpacemouseDevice::update(float deltaTime, bool jointsCaptured) { // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached } -SpacemouseManager& SpacemouseManager::getInstance() { - static SpacemouseManager sharedInstance; - if (instance == nullptr) { - new SpacemouseDevice(); - } - return sharedInstance; -} - void SpacemouseManager::ManagerFocusOutEvent() { instance->focusOutEvent(); } diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h index 08ac954c94..6253fa7f9d 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/libraries/input-plugins/src/input-plugins/SpacemouseManager.h @@ -23,7 +23,6 @@ class SpacemouseManager : public QObject { Q_OBJECT public: - static SpacemouseManager& getInstance(); void ManagerFocusOutEvent(); void init(); void destroy() {}; @@ -92,7 +91,6 @@ class SpacemouseManager : public QObject, public QAbstractNativeEventFilter { public: SpacemouseManager() {}; - static SpacemouseManager& getInstance(); void init(); void destroy(); bool Is3dmouseAttached(); @@ -169,7 +167,6 @@ private: class SpacemouseManager : public QObject { Q_OBJECT public: - static SpacemouseManager& getInstance(); void init(); void destroy(); bool Is3dmouseAttached(); diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp index 69b2b5b2c6..ec0c35cc96 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp @@ -27,12 +27,13 @@ #include #ifdef Q_OS_WIN -extern vr::IVRSystem* _hmd; -extern int hmdRefCount; extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; #endif +vr::IVRSystem* acquireOpenVrSystem(); +void releaseOpenVrSystem(); + const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; @@ -44,28 +45,11 @@ 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), - _modelLoaded(false), - _leftHandRenderID(0), - _rightHandRenderID(0), - _renderControllers(false) -{ - instance = std::shared_ptr(this); -} - bool ViveControllerManager::isSupported() const { #ifdef Q_OS_WIN - bool success = vr::VR_IsHmdPresent(); - if (success) { - vr::HmdError eError = vr::HmdError_None; - auto hmd = vr::VR_Init(&eError); - success = (hmd != nullptr); - vr::VR_Shutdown(); - } + auto hmd = acquireOpenVrSystem(); + bool success = hmd != nullptr; + releaseOpenVrSystem(); return success; #else return false; @@ -80,11 +64,8 @@ void ViveControllerManager::activate() { [this] (bool clicked) { this->setRenderControllers(clicked); }, true, true); - hmdRefCount++; if (!_hmd) { - vr::HmdError eError = vr::HmdError_None; - _hmd = vr::VR_Init(&eError); - Q_ASSERT(eError == vr::HmdError_None); + _hmd = acquireOpenVrSystem(); } Q_ASSERT(_hmd); @@ -138,7 +119,7 @@ void ViveControllerManager::activate() { // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); + userInputMapper->registerDevice(_inputDevice); _registeredWithInputMapper = true; } @@ -149,18 +130,17 @@ void ViveControllerManager::deactivate() { _container->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); _container->removeMenu(MENU_PATH); - hmdRefCount--; - - if (hmdRefCount == 0 && _hmd) { - vr::VR_Shutdown(); + if (_hmd) { + releaseOpenVrSystem(); _hmd = nullptr; } - _poseStateMap.clear(); + + _inputDevice->_poseStateMap.clear(); #endif // unregister with UserInputMapper auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(_deviceID); + userInputMapper->removeDevice(_inputDevice->_deviceID); _registeredWithInputMapper = false; } @@ -177,8 +157,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint //pendingChanges.updateItem(_leftHandRenderID, ); - controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; - controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; + controller::Pose leftHand = _inputDevice->_poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; + controller::Pose rightHand = _inputDevice->_poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { auto geometryCache = DependencyManager::get(); @@ -223,15 +203,28 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); } -void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { + +void ViveControllerManager::pluginUpdate(float deltaTime, bool jointsCaptured) { + _inputDevice->update(deltaTime, jointsCaptured); + auto userInputMapper = DependencyManager::get(); + + if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) { + userInputMapper->removeDevice(_inputDevice->_deviceID); + _registeredWithInputMapper = false; + _inputDevice->_poseStateMap.clear(); + } + + if (!_registeredWithInputMapper && _inputDevice->_trackedControllers > 0) { + userInputMapper->registerDevice(_inputDevice); + _registeredWithInputMapper = true; + UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); + } +} + +void ViveControllerManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef Q_OS_WIN _poseStateMap.clear(); - // TODO: This shouldn't be necessary - if (!_hmd) { - return; - } - _buttonPressedMap.clear(); PerformanceTimer perfTimer("ViveControllerManager::update"); @@ -279,33 +272,17 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { } } - auto userInputMapper = DependencyManager::get(); - - if (numTrackedControllers == 0) { - if (_registeredWithInputMapper) { - userInputMapper->removeDevice(_deviceID); - _registeredWithInputMapper = false; - _poseStateMap.clear(); - } - } - - if (_trackedControllers == 0 && numTrackedControllers > 0) { - userInputMapper->registerDevice(instance); - _registeredWithInputMapper = true; - UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); - } - _trackedControllers = numTrackedControllers; #endif } -void ViveControllerManager::focusOutEvent() { +void ViveControllerManager::InputDevice::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); }; // 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) { +void ViveControllerManager::InputDevice::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; @@ -320,7 +297,7 @@ void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, boo } // These functions do translation from the Steam IDs to the standard controller IDs -void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) { +void ViveControllerManager::InputDevice::handleButtonEvent(uint32_t button, bool pressed, bool left) { #ifdef Q_OS_WIN if (!pressed) { return; @@ -342,7 +319,7 @@ void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, boo #endif } -void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { +void ViveControllerManager::InputDevice::handlePoseEvent(const mat4& mat, bool left) { glm::vec3 position = extractTranslation(mat); glm::quat rotation = glm::quat_cast(mat); @@ -409,7 +386,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation); } -controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const { +controller::Input::NamedVector ViveControllerManager::InputDevice::getAvailableInputs() const { using namespace controller; QVector availableInputs{ // Trackpad analogs @@ -450,7 +427,7 @@ controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const return availableInputs; } -QString ViveControllerManager::getDefaultMappingConfig() const { +QString ViveControllerManager::InputDevice::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 938f2f9ba9..a925733327 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -24,10 +24,15 @@ #include #include -class ViveControllerManager : public InputPlugin, public controller::InputDevice { +namespace vr { + class IVRSystem; +} + +class ViveControllerManager : public InputPlugin { Q_OBJECT public: - ViveControllerManager(); + ViveControllerManager() {} + virtual ~ViveControllerManager() {} // Plugin functions virtual bool isSupported() const override; @@ -37,40 +42,51 @@ public: virtual void activate() override; virtual void deactivate() override; - virtual void pluginFocusOutEvent() override { focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } - - // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, bool jointsCaptured) override; - virtual void focusOutEvent() override; + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges); void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } private: + class InputDevice : public controller::InputDevice { + public: + InputDevice(vr::IVRSystem*& hmd) : controller::InputDevice("Vive"), _hmd(hmd) {} + private: + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, bool jointsCaptured) override; + virtual void focusOutEvent() override; + + 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 { 0 }; + vr::IVRSystem*& _hmd; + friend class ViveControllerManager; + }; + void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); - 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; - bool _modelLoaded; + bool _registeredWithInputMapper { false }; + bool _modelLoaded { false }; model::Geometry _modelGeometry; gpu::TexturePointer _texture; - int _leftHandRenderID; - int _rightHandRenderID; + int _leftHandRenderID { 0 }; + int _rightHandRenderID { 0 }; - bool _renderControllers; + bool _renderControllers { false }; + vr::IVRSystem* _hmd { nullptr }; + std::shared_ptr _inputDevice { std::make_shared(_hmd) }; static const QString NAME; - bool _registeredWithInputMapper { false }; }; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 6e2732f1d8..6cc2cfc6eb 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,7 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)); + userInputMapper->registerDevice(dynamic_cast(inputPlugin.get())->getInputDevice()); } inputPlugin->pluginUpdate(0, false); } From 619746676b39770d38d5b04165cf352446377591 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 6 Nov 2015 15:31:03 -0800 Subject: [PATCH 0755/1003] Fix ScriptAvatar (ac agent), and provide a simple working example assignment client script. --- .../src/avatars/ScriptableAvatar.cpp | 20 +++++-------- examples/acScripts/animatedAvatarAgent.js | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 examples/acScripts/animatedAvatarAgent.js diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 161be954ff..a78939256d 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -73,24 +73,20 @@ void ScriptableAvatar::update(float deltatime) { const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); - for (int i = 0; i < modelJoints.size(); i++) { - int mapping = animationJoints.indexOf(modelJoints[i]); - if (mapping != -1 && !_maskedJoints.contains(modelJoints[i])) { - JointData& data = _jointData[i]; + for (int i = 0; i < animationJoints.size(); i++) { + const QString& name = animationJoints[i]; + int mapping = getJointIndex(name); + if (mapping != -1 && !_maskedJoints.contains(name)) { + JointData& data = _jointData[mapping]; auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); - auto newTranslation = floorFrame.translations.at(i) * (1.0f - frameFraction) + - ceilFrame.translations.at(i) * frameFraction; - + // We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in), + // but we don't do so for MyAvatar yet, so let's not be different here. if (data.rotation != newRotation) { data.rotation = newRotation; data.rotationSet = true; } - if (data.translation != newTranslation) { - data.translation = newTranslation; - data.translationSet = true; - } - } + } } } else { _animation.clear(); diff --git a/examples/acScripts/animatedAvatarAgent.js b/examples/acScripts/animatedAvatarAgent.js new file mode 100644 index 0000000000..4e550e9789 --- /dev/null +++ b/examples/acScripts/animatedAvatarAgent.js @@ -0,0 +1,29 @@ +"use strict"; +/*jslint vars: true, plusplus: true*/ +/*global Agent, Avatar, Script, Entities, Vec3, print*/ +// +// animatedAvatar.js +// examples/acScripts +// +// Created by Howard Stearns 11/6/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 +// +// An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'. +// In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'. + +var origin = {x: 500, y: 502, z: 500}; +var spread = 10; // meters +var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35}; +Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy +Avatar.displayName = "'Bot"; +var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start. + +Agent.isAvatar = true; +Script.setTimeout(function () { + Avatar.position = Vec3.sum(origin, {x: Math.random() * spread, y: 0, z: Math.random() * spread}); + print("Starting at", JSON.stringify(Avatar.position)); + Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame); +}, millisecondsToWaitBeforeStarting); From 9a484ff00dd71b94c34712759fc4a863a99c910a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 6 Nov 2015 15:33:06 -0800 Subject: [PATCH 0756/1003] Fixes for windows --- tools/cache-extract/CMakeLists.txt | 1 + tools/cache-extract/src/CacheExtractApp.cpp | 22 ++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/tools/cache-extract/CMakeLists.txt b/tools/cache-extract/CMakeLists.txt index 045fa996d2..1aaa4d9d04 100644 --- a/tools/cache-extract/CMakeLists.txt +++ b/tools/cache-extract/CMakeLists.txt @@ -3,4 +3,5 @@ set(TARGET_NAME cache-extract) setup_hifi_project() link_hifi_libraries() +copy_dlls_beside_windows_executable() diff --git a/tools/cache-extract/src/CacheExtractApp.cpp b/tools/cache-extract/src/CacheExtractApp.cpp index a0d0bf54ec..0cfaef3f83 100644 --- a/tools/cache-extract/src/CacheExtractApp.cpp +++ b/tools/cache-extract/src/CacheExtractApp.cpp @@ -14,6 +14,7 @@ #include #include #include +#include // extracted from qnetworkdiskcache.cpp #define CACHE_VERSION 8 @@ -21,19 +22,18 @@ enum { CacheMagic = 0xe8, CurrentCacheVersion = CACHE_VERSION }; -#define DATA_DIR QLatin1String("data") CacheExtractApp::CacheExtractApp(int& argc, char** argv) : QCoreApplication(argc, argv) { QString myDataLoc = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - int lastSlash = myDataLoc.lastIndexOf(QDir::separator()); - QString cachePath = myDataLoc.leftRef(lastSlash).toString() + QDir::separator() + - "High Fidelity" + QDir::separator() + "Interface" + QDir::separator() + - DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/'); + int lastSlash = myDataLoc.lastIndexOf("/"); + QString cachePath = myDataLoc.leftRef(lastSlash).toString() + "/" + + "High Fidelity" + "/" + "Interface" + "/" + + "data" + QString::number(CACHE_VERSION) + "/"; - QString outputPath = myDataLoc.leftRef(lastSlash).toString() + QDir::separator() + - "High Fidelity" + QDir::separator() + "Interface" + QDir::separator() + "extracted"; + QString outputPath = myDataLoc.leftRef(lastSlash).toString() + "/" + + "High Fidelity" + "/" + "Interface" + "/" + "extracted"; qDebug() << "Searching cachePath = " << cachePath << "..."; @@ -60,9 +60,9 @@ CacheExtractApp::CacheExtractApp(int& argc, char** argv) : MyMetaData metaData; if (extractFile(fileList.at(i), metaData, contents)) { QString outFileName = outputPath + metaData.url.path(); - int lastSlash = outFileName.lastIndexOf(QDir::separator()); + int lastSlash = outFileName.lastIndexOf("/"); QString outDirName = outFileName.leftRef(lastSlash).toString(); - QDir dir(outputPath); + QDir dir; dir.mkpath(outDirName); QFile out(outFileName); if (out.open(QIODevice::WriteOnly)) { @@ -70,6 +70,9 @@ CacheExtractApp::CacheExtractApp(int& argc, char** argv) : out.close(); qDebug().noquote() << metaData.url.toDisplayString(); } + else { + qCritical() << "Error opening outputFile = " << outFileName; + } } else { qCritical() << "Error extracting = " << fileList.at(i); } @@ -123,4 +126,5 @@ QDataStream &operator>>(QDataStream& in, MyMetaData& metaData) { in >> metaData.saveToDisk; in >> metaData.attributes; in >> metaData.rawHeaders; + return in; } From f7d4a3821fd7a4d611986917db55779b30b2d7bc Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 15:42:56 -0800 Subject: [PATCH 0757/1003] add more methods --- examples/libraries/stringHelpers.js | 428 +++++++++++++++++++++++++++- 1 file changed, 419 insertions(+), 9 deletions(-) diff --git a/examples/libraries/stringHelpers.js b/examples/libraries/stringHelpers.js index 0fae9035f0..81f990ef7f 100644 --- a/examples/libraries/stringHelpers.js +++ b/examples/libraries/stringHelpers.js @@ -1,39 +1,36 @@ - - - if (typeof String.prototype.fileName !== "function") { - String.prototype.fileName = function () { + String.prototype.fileName = function() { return this.replace(/^(.*[\/\\])*/, ""); }; } if (typeof String.prototype.fileBase !== "function") { - String.prototype.fileBase = function () { + String.prototype.fileBase = function() { var filename = this.fileName(); return filename.slice(0, filename.indexOf(".")); }; } if (typeof String.prototype.fileType !== "function") { - String.prototype.fileType = function () { + String.prototype.fileType = function() { return this.slice(this.lastIndexOf(".") + 1); }; } if (typeof String.prototype.path !== "function") { - String.prototype.path = function () { + String.prototype.path = function() { return this.replace(/[\\\/][^\\\/]*$/, ""); }; } if (typeof String.prototype.regExpEscape !== "function") { - String.prototype.regExpEscape = function () { + String.prototype.regExpEscape = function() { return this.replace(/([$\^.+*?|\\\/{}()\[\]])/g, '\\$1'); }; } if (typeof String.prototype.toArrayBuffer !== "function") { - String.prototype.toArrayBuffer = function () { + String.prototype.toArrayBuffer = function() { var length, buffer, view, @@ -64,3 +61,416 @@ if (typeof String.prototype.toArrayBuffer !== "function") { return buffer; }; } +// Copyright Mathias Bynens + +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: + +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/*! https://mths.be/includes v1.0.0 by @mathias */ +if (!String.prototype.includes) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var toString = {}.toString; + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var indexOf = ''.indexOf; + var includes = function(search) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + return indexOf.call(string, searchString, pos) != -1; + }; + if (defineProperty) { + defineProperty(String.prototype, 'includes', { + 'value': includes, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.includes = includes; + } + }()); +} + +/*! https://mths.be/startswith v0.2.0 by @mathias */ +if (!String.prototype.startsWith) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var toString = {}.toString; + var startsWith = function(search) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'startsWith', { + 'value': startsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.startsWith = startsWith; + } + }()); +} +if (!String.prototype.endsWith) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var toString = {}.toString; + var endsWith = function(search) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var pos = stringLength; + if (arguments.length > 1) { + var position = arguments[1]; + if (position !== undefined) { + // `ToInteger` + pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + } + } + var end = Math.min(Math.max(pos, 0), stringLength); + var start = end - searchLength; + if (start < 0) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'endsWith', { + 'value': endsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.endsWith = endsWith; + } + }()); +} + +/*! https://mths.be/repeat v0.2.0 by @mathias */ +if (!String.prototype.repeat) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var repeat = function(count) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + // `ToInteger` + var n = count ? Number(count) : 0; + if (n != n) { // better `isNaN` + n = 0; + } + // Account for out-of-bounds indices + if (n < 0 || n == Infinity) { + throw RangeError(); + } + var result = ''; + while (n) { + if (n % 2 == 1) { + result += string; + } + if (n > 1) { + string += string; + } + n >>= 1; + } + return result; + }; + if (defineProperty) { + defineProperty(String.prototype, 'repeat', { + 'value': repeat, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.repeat = repeat; + } + }()); +} + +if (!String.prototype.at) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements. + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (exception) {} + return result; + }()); + var at = function(position) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + var size = string.length; + // `ToInteger` + var index = position ? Number(position) : 0; + if (index != index) { // better `isNaN` + index = 0; + } + // Account for out-of-bounds indices + // The odd lower bound is because the ToInteger operation is + // going to round `n` to `0` for `-1 < n <= 0`. + if (index <= -1 || index >= size) { + return ''; + } + // Second half of `ToInteger` + index = index | 0; + // Get the first code unit and code unit value + var cuFirst = string.charCodeAt(index); + var cuSecond; + var nextIndex = index + 1; + var len = 1; + if ( // Check if it’s the start of a surrogate pair. + cuFirst >= 0xD800 && cuFirst <= 0xDBFF && // high surrogate + size > nextIndex // there is a next code unit + ) { + cuSecond = string.charCodeAt(nextIndex); + if (cuSecond >= 0xDC00 && cuSecond <= 0xDFFF) { // low surrogate + len = 2; + } + } + return string.slice(index, index + len); + }; + if (defineProperty) { + defineProperty(String.prototype, 'at', { + 'value': at, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.at = at; + } + }()); +} + +/*! https://mths.be/codepointat v0.2.0 by @mathias */ +if (!String.prototype.codePointAt) { + (function() { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var codePointAt = function(position) { + if (this == null) { + throw TypeError(); + } + var string = String(this); + var size = string.length; + // `ToInteger` + var index = position ? Number(position) : 0; + if (index != index) { // better `isNaN` + index = 0; + } + // Account for out-of-bounds indices: + if (index < 0 || index >= size) { + return undefined; + } + // Get the first code unit + var first = string.charCodeAt(index); + var second; + if ( // check if it’s the start of a surrogate pair + first >= 0xD800 && first <= 0xDBFF && // high surrogate + size > index + 1 // there is a next code unit + ) { + second = string.charCodeAt(index + 1); + if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } + } + return first; + }; + if (defineProperty) { + defineProperty(String.prototype, 'codePointAt', { + 'value': codePointAt, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.codePointAt = codePointAt; + } + }()); +} + +/*! https://mths.be/fromcodepoint v0.2.1 by @mathias */ +if (!String.fromCodePoint) { + (function() { + var defineProperty = (function() { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) {} + return result; + }()); + var stringFromCharCode = String.fromCharCode; + var floor = Math.floor; + var fromCodePoint = function(_) { + var MAX_SIZE = 0x4000; + var codeUnits = []; + var highSurrogate; + var lowSurrogate; + var index = -1; + var length = arguments.length; + if (!length) { + return ''; + } + var result = ''; + while (++index < length) { + var codePoint = Number(arguments[index]); + if (!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity` + codePoint < 0 || // not a valid Unicode code point + codePoint > 0x10FFFF || // not a valid Unicode code point + floor(codePoint) != codePoint // not an integer + ) { + throw RangeError('Invalid code point: ' + codePoint); + } + if (codePoint <= 0xFFFF) { // BMP code point + codeUnits.push(codePoint); + } else { // Astral code point; split in surrogate halves + // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae + codePoint -= 0x10000; + highSurrogate = (codePoint >> 10) + 0xD800; + lowSurrogate = (codePoint % 0x400) + 0xDC00; + codeUnits.push(highSurrogate, lowSurrogate); + } + if (index + 1 == length || codeUnits.length > MAX_SIZE) { + result += stringFromCharCode.apply(null, codeUnits); + codeUnits.length = 0; + } + } + return result; + }; + if (defineProperty) { + defineProperty(String, 'fromCodePoint', { + 'value': fromCodePoint, + 'configurable': true, + 'writable': true + }); + } else { + String.fromCodePoint = fromCodePoint; + } + }()); +} \ No newline at end of file From bf70ae4724fd16cd04d220d0a76a5481bc800929 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 15:59:00 -0800 Subject: [PATCH 0758/1003] PR feedback --- .../src/display-plugins/Logging.cpp | 11 +++++++++++ .../src/display-plugins/Logging.h | 16 ++++++++++++++++ .../src/display-plugins/openvr/OpenVrHelpers.cpp | 8 +++++--- .../src/input-plugins/KeyboardMouseDevice.h | 10 ++++------ .../src/input-plugins/SixenseManager.h | 4 +--- .../src/input-plugins/ViveControllerManager.h | 2 -- tests/controllers/src/main.cpp | 2 +- 7 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 libraries/display-plugins/src/display-plugins/Logging.cpp create mode 100644 libraries/display-plugins/src/display-plugins/Logging.h diff --git a/libraries/display-plugins/src/display-plugins/Logging.cpp b/libraries/display-plugins/src/display-plugins/Logging.cpp new file mode 100644 index 0000000000..00cd4cf0f9 --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/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(displayPlugins, "hifi.plugins.display") diff --git a/libraries/display-plugins/src/display-plugins/Logging.h b/libraries/display-plugins/src/display-plugins/Logging.h new file mode 100644 index 0000000000..79b20b5dcc --- /dev/null +++ b/libraries/display-plugins/src/display-plugins/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_DisplayPlugins_Logging_h +#define hifi_DisplayPlugins_Logging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(displayPlugins) + +#endif diff --git a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp index 44d30962bd..f8e810beaf 100644 --- a/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp +++ b/libraries/display-plugins/src/display-plugins/openvr/OpenVrHelpers.cpp @@ -14,6 +14,8 @@ #include #include +#include "../Logging.h" + using Mutex = std::mutex; using Lock = std::unique_lock; @@ -28,13 +30,13 @@ vr::IVRSystem* acquireOpenVrSystem() { if (hmdPresent) { Lock lock(mutex); if (!activeHmd) { - qDebug() << "openvr: No vr::IVRSystem instance active, building"; + qCDebug(displayPlugins) << "openvr: No vr::IVRSystem instance active, building"; vr::HmdError eError = vr::HmdError_None; activeHmd = vr::VR_Init(&eError); - qDebug() << "openvr display: HMD is " << activeHmd << " error is " << eError; + qCDebug(displayPlugins) << "openvr display: HMD is " << activeHmd << " error is " << eError; } if (activeHmd) { - qDebug() << "openvr: incrementing refcount"; + qCDebug(displayPlugins) << "openvr: incrementing refcount"; ++refCount; } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b0578e3a99..4abdc44478 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -64,12 +64,6 @@ public: TOUCH_BUTTON_PRESS = TOUCH_AXIS_Y_NEG + 1, }; - KeyboardMouseDevice() {} - - virtual ~KeyboardMouseDevice() {} - - controller::InputDevice::Pointer getInputDevice() { return _inputDevice; } - // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } @@ -116,6 +110,10 @@ protected: friend class KeyboardMouseDevice; }; +public: + const std::shared_ptr& getInputDevice() const { return _inputDevice; } + +protected: QPoint _lastCursor; QPoint _mousePressAt; glm::vec2 _lastTouch; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 7a686dc423..062a27390c 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -47,9 +47,7 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; class SixenseManager : public InputPlugin { Q_OBJECT public: - SixenseManager() {} - virtual ~SixenseManager() {} - + // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } diff --git a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h index a925733327..02bdecb10a 100644 --- a/libraries/input-plugins/src/input-plugins/ViveControllerManager.h +++ b/libraries/input-plugins/src/input-plugins/ViveControllerManager.h @@ -31,8 +31,6 @@ namespace vr { class ViveControllerManager : public InputPlugin { Q_OBJECT public: - ViveControllerManager() {} - virtual ~ViveControllerManager() {} // Plugin functions virtual bool isSupported() const override; diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 6cc2cfc6eb..664a894e44 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -132,7 +132,7 @@ int main(int argc, char** argv) { inputPlugin->activate(); auto userInputMapper = DependencyManager::get(); if (name == KeyboardMouseDevice::NAME) { - userInputMapper->registerDevice(dynamic_cast(inputPlugin.get())->getInputDevice()); + userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)->getInputDevice()); } inputPlugin->pluginUpdate(0, false); } From 5893bfeb06ab622383f0f81514708b0183a3b3d9 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 16:19:49 -0800 Subject: [PATCH 0759/1003] add user timing --- examples/libraries/usertiming.js | 547 ++++++++++++++++++++++++ examples/libraries/usertimingExample.js | 16 + 2 files changed, 563 insertions(+) create mode 100644 examples/libraries/usertiming.js create mode 100644 examples/libraries/usertimingExample.js diff --git a/examples/libraries/usertiming.js b/examples/libraries/usertiming.js new file mode 100644 index 0000000000..61554384e1 --- /dev/null +++ b/examples/libraries/usertiming.js @@ -0,0 +1,547 @@ +/* eslint-env browser,amd,node */ +// +// usertiming.js +// +// A polyfill for UserTiming (http://www.w3.org/TR/user-timing/) +// +// Copyright 2013 Nic Jansma +// http://nicj.net +// +// https://github.com/nicjansma/usertiming.js +// +// Licensed under the MIT license +// + +function userTiming() { + "use strict"; + + // allow running in Node.js environment + if (typeof window === "undefined") { + window = {}; + } + + // prepare base perf object + if (typeof window.performance === "undefined") { + window.performance = {}; + } + + // We need to keep a global reference to the window.performance object to + // prevent any added properties from being garbage-collected in Safari 8. + // https://bugs.webkit.org/show_bug.cgi?id=137407 + window._perfRefForUserTimingPolyfill = window.performance; + + // + // Note what we shimmed + // + window.performance.userTimingJsNow = false; + window.performance.userTimingJsNowPrefixed = false; + window.performance.userTimingJsUserTiming = false; + window.performance.userTimingJsUserTimingPrefixed = false; + window.performance.userTimingJsPerformanceTimeline = false; + window.performance.userTimingJsPerformanceTimelinePrefixed = false; + + // for prefixed support + var prefixes = []; + var methods = []; + var methodTest = null; + var i, j; + + // + // window.performance.now() shim + // http://www.w3.org/TR/hr-time/ + // + if (typeof window.performance.now !== "function") { + window.performance.userTimingJsNow = true; + + // copy prefixed version over if it exists + methods = ["webkitNow", "msNow", "mozNow"]; + + for (i = 0; i < methods.length; i++) { + if (typeof window.performance[methods[i]] === "function") { + window.performance.now = window.performance[methods[i]]; + + window.performance.userTimingJsNowPrefixed = true; + + break; + } + } + + // + // now() should be a DOMHighResTimeStamp, which is defined as being a time relative + // to navigationStart of the PerformanceTiming (PT) interface. If this browser supports + // PT, use that as our relative start. Otherwise, use "now" as the start and all other + // now() calls will be relative to our initialization. + // + + var nowOffset = +(new Date()); + if (window.performance.timing && window.performance.timing.navigationStart) { + nowOffset = window.performance.timing.navigationStart; + } + + if (typeof window.performance.now !== "function") { + // No browser support, fall back to Date.now + if (Date.now) { + window.performance.now = function() { + return Date.now() - nowOffset; + }; + } else { + // no Date.now support, get the time from new Date() + window.performance.now = function() { + return +(new Date()) - nowOffset; + }; + } + } + } + + // + // PerformanceTimeline (PT) shims + // http://www.w3.org/TR/performance-timeline/ + // + + /** + * Adds an object to our internal Performance Timeline array. + * + * Will be blank if the environment supports PT. + */ + var addToPerformanceTimeline = function() {}; + + /** + * Clears the specified entry types from our timeline array. + * + * Will be blank if the environment supports PT. + */ + var clearEntriesFromPerformanceTimeline = function() {}; + + // performance timeline array + var performanceTimeline = []; + + // whether or not the timeline will require sort on getEntries() + var performanceTimelineRequiresSort = false; + + // whether or not ResourceTiming is natively supported but UserTiming is + // not (eg Firefox 35) + var hasNativeGetEntriesButNotUserTiming = false; + + // + // If getEntries() and mark() aren't defined, we'll assume + // we have to shim at least some PT functions. + // + if (typeof window.performance.getEntries !== "function" || + typeof window.performance.mark !== "function") { + + if (typeof window.performance.getEntries === "function" && + typeof window.performance.mark !== "function") { + hasNativeGetEntriesButNotUserTiming = true; + } + + window.performance.userTimingJsPerformanceTimeline = true; + + // copy prefixed version over if it exists + prefixes = ["webkit", "moz"]; + methods = ["getEntries", "getEntriesByName", "getEntriesByType"]; + + for (i = 0; i < methods.length; i++) { + for (j = 0; j < prefixes.length; j++) { + // prefixed method will likely have an upper-case first letter + methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1); + + if (typeof window.performance[methodTest] === "function") { + window.performance[methods[i]] = window.performance[methodTest]; + + window.performance.userTimingJsPerformanceTimelinePrefixed = true; + } + } + } + + /** + * Adds an object to our internal Performance Timeline array. + * + * @param {Object} obj PerformanceEntry + */ + addToPerformanceTimeline = function(obj) { + performanceTimeline.push(obj); + + // + // If we insert a measure, its startTime may be out of order + // from the rest of the entries because the use can use any + // mark as the start time. If so, note we have to sort it before + // returning getEntries(); + // + if (obj.entryType === "measure") { + performanceTimelineRequiresSort = true; + } + }; + + /** + * Ensures our PT array is in the correct sorted order (by startTime) + */ + var ensurePerformanceTimelineOrder = function() { + if (!performanceTimelineRequiresSort) { + return; + } + + // + // Measures, which may be in this list, may enter the list in + // an unsorted order. For example: + // + // 1. measure("a") + // 2. mark("start_mark") + // 3. measure("b", "start_mark") + // 4. measure("c") + // 5. getEntries() + // + // When calling #5, we should return [a,c,b] because technically the start time + // of c is "0" (navigationStart), which will occur before b's start time due to the mark. + // + performanceTimeline.sort(function(a, b) { + return a.startTime - b.startTime; + }); + + performanceTimelineRequiresSort = false; + }; + + /** + * Clears the specified entry types from our timeline array. + * + * @param {string} entryType Entry type (eg "mark" or "measure") + * @param {string} [name] Entry name (optional) + */ + clearEntriesFromPerformanceTimeline = function(entryType, name) { + // clear all entries from the perf timeline + i = 0; + while (i < performanceTimeline.length) { + if (performanceTimeline[i].entryType !== entryType) { + // unmatched entry type + i++; + continue; + } + + if (typeof name !== "undefined" && performanceTimeline[i].name !== name) { + // unmatched name + i++; + continue; + } + + // this entry matches our criteria, remove just it + performanceTimeline.splice(i, 1); + } + }; + + if (typeof window.performance.getEntries !== "function" || hasNativeGetEntriesButNotUserTiming) { + var origGetEntries = window.performance.getEntries; + + /** + * Gets all entries from the Performance Timeline. + * http://www.w3.org/TR/performance-timeline/#dom-performance-getentries + * + * NOTE: This will only ever return marks and measures. + * + * @returns {PerformanceEntry[]} Array of PerformanceEntrys + */ + window.performance.getEntries = function() { + ensurePerformanceTimelineOrder(); + + // get a copy of all of our entries + var entries = performanceTimeline.slice(0); + + // if there was a native version of getEntries, add that + if (hasNativeGetEntriesButNotUserTiming && origGetEntries) { + // merge in native + Array.prototype.push.apply(entries, origGetEntries.call(window.performance)); + + // sort by startTime + entries.sort(function(a, b) { + return a.startTime - b.startTime; + }); + } + + return entries; + }; + } + + if (typeof window.performance.getEntriesByType !== "function" || hasNativeGetEntriesButNotUserTiming) { + var origGetEntriesByType = window.performance.getEntriesByType; + + /** + * Gets all entries from the Performance Timeline of the specified type. + * http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbytype + * + * NOTE: This will only work for marks and measures. + * + * @param {string} entryType Entry type (eg "mark" or "measure") + * + * @returns {PerformanceEntry[]} Array of PerformanceEntrys + */ + window.performance.getEntriesByType = function(entryType) { + // we only support marks/measures + if (typeof entryType === "undefined" || + (entryType !== "mark" && entryType !== "measure")) { + + if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByType) { + // native version exists, forward + return origGetEntriesByType.call(window.performance, entryType); + } + + return []; + } + + // see note in ensurePerformanceTimelineOrder() on why this is required + if (entryType === "measure") { + ensurePerformanceTimelineOrder(); + } + + // find all entries of entryType + var entries = []; + for (i = 0; i < performanceTimeline.length; i++) { + if (performanceTimeline[i].entryType === entryType) { + entries.push(performanceTimeline[i]); + } + } + + return entries; + }; + } + + if (typeof window.performance.getEntriesByName !== "function" || hasNativeGetEntriesButNotUserTiming) { + var origGetEntriesByName = window.performance.getEntriesByName; + + /** + * Gets all entries from the Performance Timeline of the specified + * name, and optionally, type. + * http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbyname + * + * NOTE: This will only work for marks and measures. + * + * @param {string} name Entry name + * @param {string} [entryType] Entry type (eg "mark" or "measure") + * + * @returns {PerformanceEntry[]} Array of PerformanceEntrys + */ + window.performance.getEntriesByName = function(name, entryType) { + if (entryType && entryType !== "mark" && entryType !== "measure") { + if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) { + // native version exists, forward + return origGetEntriesByName.call(window.performance, name, entryType); + } + + return []; + } + + // see note in ensurePerformanceTimelineOrder() on why this is required + if (typeof entryType !== "undefined" && entryType === "measure") { + ensurePerformanceTimelineOrder(); + } + + // find all entries of the name and (optionally) type + var entries = []; + for (i = 0; i < performanceTimeline.length; i++) { + if (typeof entryType !== "undefined" && + performanceTimeline[i].entryType !== entryType) { + continue; + } + + if (performanceTimeline[i].name === name) { + entries.push(performanceTimeline[i]); + } + } + + if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) { + // merge in native + Array.prototype.push.apply(entries, origGetEntriesByName.call(window.performance, name, entryType)); + + // sort by startTime + entries.sort(function(a, b) { + return a.startTime - b.startTime; + }); + } + + return entries; + }; + } + } + + // + // UserTiming support + // + if (typeof window.performance.mark !== "function") { + window.performance.userTimingJsUserTiming = true; + + // copy prefixed version over if it exists + prefixes = ["webkit", "moz", "ms"]; + methods = ["mark", "measure", "clearMarks", "clearMeasures"]; + + for (i = 0; i < methods.length; i++) { + for (j = 0; j < prefixes.length; j++) { + // prefixed method will likely have an upper-case first letter + methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1); + + if (typeof window.performance[methodTest] === "function") { + window.performance[methods[i]] = window.performance[methodTest]; + + window.performance.userTimingJsUserTimingPrefixed = true; + } + } + } + + // only used for measure(), to quickly see the latest timestamp of a mark + var marks = {}; + + if (typeof window.performance.mark !== "function") { + /** + * UserTiming mark + * http://www.w3.org/TR/user-timing/#dom-performance-mark + * + * @param {string} markName Mark name + */ + window.performance.mark = function(markName) { + var now = window.performance.now(); + + // mark name is required + if (typeof markName === "undefined") { + throw new SyntaxError("Mark name must be specified"); + } + + // mark name can't be a NT timestamp + if (window.performance.timing && markName in window.performance.timing) { + throw new SyntaxError("Mark name is not allowed"); + } + + if (!marks[markName]) { + marks[markName] = []; + } + + marks[markName].push(now); + + // add to perf timeline as well + addToPerformanceTimeline({ + entryType: "mark", + name: markName, + startTime: now, + duration: 0 + }); + }; + } + + if (typeof window.performance.clearMarks !== "function") { + /** + * UserTiming clear marks + * http://www.w3.org/TR/user-timing/#dom-performance-clearmarks + * + * @param {string} markName Mark name + */ + window.performance.clearMarks = function(markName) { + if (!markName) { + // clear all marks + marks = {}; + } else { + marks[markName] = []; + } + + clearEntriesFromPerformanceTimeline("mark", markName); + }; + } + + if (typeof window.performance.measure !== "function") { + /** + * UserTiming measure + * http://www.w3.org/TR/user-timing/#dom-performance-measure + * + * @param {string} measureName Measure name + * @param {string} [startMark] Start mark name + * @param {string} [endMark] End mark name + */ + window.performance.measure = function(measureName, startMark, endMark) { + var now = window.performance.now(); + + if (typeof measureName === "undefined") { + throw new SyntaxError("Measure must be specified"); + } + + // if there isn't a startMark, we measure from navigationStart to now + if (!startMark) { + // add to perf timeline as well + addToPerformanceTimeline({ + entryType: "measure", + name: measureName, + startTime: 0, + duration: now + }); + + return; + } + + // + // If there is a startMark, check for it first in the NavigationTiming interface, + // then check our own marks. + // + var startMarkTime = 0; + if (window.performance.timing && startMark in window.performance.timing) { + // mark cannot have a timing of 0 + if (startMark !== "navigationStart" && window.performance.timing[startMark] === 0) { + throw new Error(startMark + " has a timing of 0"); + } + + // time is the offset of this mark to navigationStart's time + startMarkTime = window.performance.timing[startMark] - window.performance.timing.navigationStart; + } else if (startMark in marks) { + startMarkTime = marks[startMark][marks[startMark].length - 1]; + } else { + throw new Error(startMark + " mark not found"); + } + + // + // If there is a endMark, check for it first in the NavigationTiming interface, + // then check our own marks. + // + var endMarkTime = now; + + if (endMark) { + endMarkTime = 0; + + if (window.performance.timing && endMark in window.performance.timing) { + // mark cannot have a timing of 0 + if (endMark !== "navigationStart" && window.performance.timing[endMark] === 0) { + throw new Error(endMark + " has a timing of 0"); + } + + // time is the offset of this mark to navigationStart's time + endMarkTime = window.performance.timing[endMark] - window.performance.timing.navigationStart; + } else if (endMark in marks) { + endMarkTime = marks[endMark][marks[endMark].length - 1]; + } else { + throw new Error(endMark + " mark not found"); + } + } + + // add to our measure array + var duration = endMarkTime - startMarkTime; + + // add to perf timeline as well + addToPerformanceTimeline({ + entryType: "measure", + name: measureName, + startTime: startMarkTime, + duration: duration + }); + }; + } + + if (typeof window.performance.clearMeasures !== "function") { + /** + * UserTiming clear measures + * http://www.w3.org/TR/user-timing/#dom-performance-clearmeasures + * + * @param {string} measureName Measure name + */ + window.performance.clearMeasures = function(measureName) { + clearEntriesFromPerformanceTimeline("measure", measureName); + }; + } + } + + return window +} + +loadUserTiming = function() { + return userTiming(); +} \ No newline at end of file diff --git a/examples/libraries/usertimingExample.js b/examples/libraries/usertimingExample.js new file mode 100644 index 0000000000..a43496a6b9 --- /dev/null +++ b/examples/libraries/usertimingExample.js @@ -0,0 +1,16 @@ +Script.include('usertiming.js'); +var timing = loadUserTiming(); +//set a mark +timing.performance.mark('mark_fully_loaded') + +var count = 0; +Script.setTimeout(function() { + //do something that takes time + //and set another mark + timing.performance.mark('mark_fully_loaded2') + //measure time between marks (first parameter is a name for the measurement) + timing.performance.measure('howlong', 'mark_fully_loaded', 'mark_fully_loaded2'); + //you can also get the marks, but the measure is usually what you're looking for + var items = timing.performance.getEntriesByType('measure'); + print('items is:::' + JSON.stringify(items)) +}, 1000) \ No newline at end of file From f478261ed0b0da3cd70bb78a3a49ba1474720c2c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 16:21:53 -0800 Subject: [PATCH 0760/1003] cleanup --- examples/libraries/usertiming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/usertiming.js b/examples/libraries/usertiming.js index 61554384e1..f477ed0bb7 100644 --- a/examples/libraries/usertiming.js +++ b/examples/libraries/usertiming.js @@ -1,9 +1,9 @@ -/* eslint-env browser,amd,node */ // // usertiming.js // // A polyfill for UserTiming (http://www.w3.org/TR/user-timing/) // +// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015 // Copyright 2013 Nic Jansma // http://nicj.net // From 4dbca687678b8075ef1084fc8b36e162586a2caa Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 6 Nov 2015 16:24:19 -0800 Subject: [PATCH 0761/1003] Updating the legacy file format to support the joint translation and also fix the replay problem oof the joints going to the moon --- libraries/avatars/src/Recorder.cpp | 10 ++++++---- libraries/avatars/src/Recording.cpp | 23 +++++++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/libraries/avatars/src/Recorder.cpp b/libraries/avatars/src/Recorder.cpp index eeaa8cb1fd..68e667604b 100644 --- a/libraries/avatars/src/Recorder.cpp +++ b/libraries/avatars/src/Recorder.cpp @@ -100,13 +100,15 @@ void Recorder::record() { const RecordingContext& context = _recording->getContext(); RecordingFrame frame; frame.setBlendshapeCoefficients(_avatar->getHeadData()->getBlendshapeCoefficients()); - frame.setJointRotations(_avatar->getJointRotations()); - frame.setJointTranslations(_avatar->getJointTranslations()); + // FIXME: here we need to make sure the correct joint data on the AvatarData to get correct play back. + // This should be fixed by a fix coming from Howard soon + frame.setJointRotations(_avatar->::AvatarData::getJointRotations()); + frame.setJointTranslations(_avatar->::AvatarData::getJointTranslations()); + frame.setTranslation(context.orientationInv * (_avatar->getPosition() - context.position)); frame.setRotation(context.orientationInv * _avatar->getOrientation()); frame.setScale(_avatar->getTargetScale() / context.scale); - - + const HeadData* head = _avatar->getHeadData(); if (head) { glm::vec3 rotationDegrees = glm::vec3(head->getFinalPitch(), diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 2e2f46552d..4ca56421e5 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -239,12 +239,21 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { if (i == 0 || frame._jointRotations[j] != previousFrame._jointRotations[j]) { writeQuat(stream, frame._jointRotations[j]); - // TODO -- handle translations mask.setBit(maskIndex); } maskIndex++; } - + + // Joint Translations + for (quint32 j = 0; j < numJoints; ++j) { + if (i == 0 || + frame._jointTranslations[j] != previousFrame._jointTranslations[j]) { + writeVec3(stream, frame._jointTranslations[j]); + mask.setBit(maskIndex); + } + maskIndex++; + } + // Translation if (i == 0) { mask.resize(mask.size() + 1); @@ -563,8 +572,14 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } } - // TODO -- handle translations - + // Joint Translations + frame._jointTranslations.resize(numJoints); + for (quint32 j = 0; j < numJoints; ++j) { + if (!mask[maskIndex++] || !readVec3(stream, frame._jointTranslations[j])) { + frame._jointTranslations[j] = previousFrame._jointTranslations[j]; + } + } + if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) { frame._translation = previousFrame._translation; } From 298ac650d8ea36f12d877d28ce35ac585e6d4c8f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 21:25:49 -0700 Subject: [PATCH 0762/1003] Isolate sixense dynamic linking for OSX --- .../src/input-plugins/SixenseManager.cpp | 101 +++--------------- .../src/input-plugins/SixenseManager.h | 18 ---- .../src/input-plugins/SixenseSupportOSX.cpp | 97 +++++++++++++++++ 3 files changed, 109 insertions(+), 107 deletions(-) create mode 100644 libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 33b4332430..24b1b60356 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -9,12 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "SixenseManager.h" + +#ifdef HAVE_SIXENSE +#include "sixense.h" +#endif #include #include #include +// TODO: This should not be here +#include +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + #include #include #include @@ -25,40 +34,12 @@ #include #include -#include "SixenseManager.h" - - -#ifdef HAVE_SIXENSE - #include "sixense.h" - -#ifdef __APPLE__ -static QLibrary* _sixenseLibrary { nullptr }; -#endif - -#endif - -// TODO: This should not be here -#include -Q_DECLARE_LOGGING_CATEGORY(inputplugins) -Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") - -#ifdef HAVE_SIXENSE - +#include "UserActivityLogger.h" const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; -#endif - -#ifdef __APPLE__ -typedef int (*SixenseBaseFunction)(); -typedef int (*SixenseTakeIntFunction)(int); -#ifdef HAVE_SIXENSE -typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*); -#endif -#endif - const QString SixenseManager::NAME = "Sixense"; const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra"; @@ -94,31 +75,6 @@ void SixenseManager::activate() { auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); -#ifdef __APPLE__ - - if (!_sixenseLibrary) { - -#ifdef SIXENSE_LIB_FILENAME - _sixenseLibrary = new QLibrary(SIXENSE_LIB_FILENAME); -#else - const QString SIXENSE_LIBRARY_NAME = "libsixense_x64"; - QString frameworkSixenseLibrary = QCoreApplication::applicationDirPath() + "/../Frameworks/" - + SIXENSE_LIBRARY_NAME; - - _sixenseLibrary = new QLibrary(frameworkSixenseLibrary); -#endif - } - - if (_sixenseLibrary->load()){ - qCDebug(inputplugins) << "Loaded sixense library for hydra support -" << _sixenseLibrary->fileName(); - } else { - qCDebug(inputplugins) << "Sixense library at" << _sixenseLibrary->fileName() << "failed to load." - << "Continuing without hydra support."; - return; - } - - SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit"); -#endif loadSettings(); sixenseInit(); #endif @@ -139,26 +95,13 @@ void SixenseManager::deactivate() { userInputMapper->removeDevice(_inputDevice->_deviceID); } -#ifdef __APPLE__ - SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit"); -#endif - sixenseExit(); - -#ifdef __APPLE__ - delete _sixenseLibrary; -#endif - #endif } void SixenseManager::setSixenseFilter(bool filter) { #ifdef HAVE_SIXENSE -#ifdef __APPLE__ - SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled"); -#endif - int newFilter = filter ? 1 : 0; - sixenseSetFilterEnabled(newFilter); + sixenseSetFilterEnabled(filter ? 1 : 0); #endif } @@ -180,11 +123,6 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); -#ifdef __APPLE__ - SixenseBaseFunction sixenseGetNumActiveControllers = - (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); -#endif - auto userInputMapper = DependencyManager::get(); static const float MAX_DISCONNECTED_TIME = 2.0f; @@ -213,24 +151,11 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // 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"); -#endif - int maxControllers = sixenseGetMaxControllers(); // we only support two controllers sixenseControllerData controllers[2]; -#ifdef __APPLE__ - SixenseTakeIntFunction sixenseIsControllerEnabled = - (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled"); - - SixenseTakeIntAndSixenseControllerData sixenseGetNewestData = - (SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData"); -#endif - int numActiveControllers = 0; for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) { if (!sixenseIsControllerEnabled(i)) { @@ -479,8 +404,6 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos glm::vec3 velocity(0.0f); glm::quat angularVelocity; - - if (prevPose.isValid() && deltaTime > std::numeric_limits::epsilon()) { velocity = (position - prevPose.getTranslation()) / deltaTime; diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 062a27390c..bf657d347b 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -12,18 +12,6 @@ #ifndef hifi_SixenseManager_h #define hifi_SixenseManager_h -#ifdef HAVE_SIXENSE - #include - #include - #include "sixense.h" - -#ifdef __APPLE__ - #include - #include -#endif - -#endif - #include #include @@ -31,8 +19,6 @@ #include "InputPlugin.h" -class QLibrary; - const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 const unsigned int BUTTON_1 = 1U << 5; const unsigned int BUTTON_2 = 1U << 6; @@ -75,7 +61,6 @@ private: static const float CONTROLLER_THRESHOLD; static const float DEFAULT_REACH_LENGTH; - using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; using MovingAverageMap = std::map< int, Samples >; @@ -113,9 +98,6 @@ private: glm::vec3 _reachRight; }; - - - bool _useSixenseFilter = true; std::shared_ptr _inputDevice { std::make_shared() }; static const QString NAME; diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp new file mode 100644 index 0000000000..c5c3f7f14a --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -0,0 +1,97 @@ +// +// SixenseSupportOSX.cpp +// +// +// Created by Clement on 10/20/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 +// + +#ifdef __APPLE__ +#include "sixense.h" + +#include +#include +#include + +using std::string; +using std::unique_ptr; +using SixenseBaseFunction = int (*)(); +using SixenseTakeIntFunction = int (*)(int); +using SixenseTakeIntAndSixenseControllerData = int (*)(int, sixenseControllerData*); + +static unique_ptr SIXENSE; + +bool loadSixense() { + if (!SIXENSE) { + static const QString LIBRARY_PATH = +#ifdef SIXENSE_LIB_FILENAME + SIXENSE_LIB_FILENAME; +#else + QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"; +#endif + SIXENSE.reset(new QLibrary(LIBRARY_PATH)); + } + + if (SIXENSE->load()){ + qDebug() << "Loaded sixense library for hydra support -" << SIXENSE->fileName(); + } else { + qDebug() << "Sixense library at" << SIXENSE->fileName() << "failed to load:" << SIXENSE->errorString(); + qDebug() << "Continuing without hydra support."; + } + return SIXENSE->isLoaded(); +} + +void unloadSixense() { + SIXENSE->unload(); +} + +template +Func resolve(const char* name) { + Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); + auto func = reinterpret_cast(SIXENSE->resolve(name)); + Q_ASSERT_X(func, __FUNCTION__, string("Could not resolve ").append(name).c_str()); + return func; +} + +// sixense.h wrapper for OSX dynamic linking +int sixenseInit() { + loadSixense(); + auto sixenseInit = resolve("sixenseInit"); + return sixenseInit(); +} + +int sixenseExit() { + auto sixenseExit = resolve("sixenseExit"); + auto returnCode = sixenseExit(); + unloadSixense(); + return returnCode; +} + +int sixenseSetFilterEnabled(int input) { + auto sixenseSetFilterEnabled = resolve("sixenseSetFilterEnabled"); + return sixenseSetFilterEnabled(input); +} + +int sixenseGetNumActiveControllers() { + auto sixenseGetNumActiveControllers = resolve("sixenseGetNumActiveControllers"); + return sixenseGetNumActiveControllers(); +} + +int sixenseGetMaxControllers() { + auto sixenseGetMaxControllers = resolve("sixenseGetMaxControllers"); + return sixenseGetMaxControllers(); +} + +int sixenseIsControllerEnabled(int input) { + auto sixenseIsControllerEnabled = resolve("sixenseIsControllerEnabled"); + return sixenseIsControllerEnabled(input); +} + +int sixenseGetNewestData(int input1, sixenseControllerData* intput2) { + auto sixenseGetNewestData = resolve("sixenseGetNewestData"); + return sixenseGetNewestData(input1, intput2); +} +#endif From 96aa5be457d1455348ffdf5e90f38898c79eaf3a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 23:06:44 -0700 Subject: [PATCH 0763/1003] Implement all Sixense methods for OS X --- .../src/input-plugins/SixenseSupportOSX.cpp | 102 +++++++++++++----- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index c5c3f7f14a..a890d7c138 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -24,7 +24,7 @@ using SixenseTakeIntAndSixenseControllerData = int (*)(int, sixenseControllerDat static unique_ptr SIXENSE; -bool loadSixense() { +void loadSixense() { if (!SIXENSE) { static const QString LIBRARY_PATH = #ifdef SIXENSE_LIB_FILENAME @@ -41,57 +41,107 @@ bool loadSixense() { qDebug() << "Sixense library at" << SIXENSE->fileName() << "failed to load:" << SIXENSE->errorString(); qDebug() << "Continuing without hydra support."; } - return SIXENSE->isLoaded(); } void unloadSixense() { SIXENSE->unload(); } -template -Func resolve(const char* name) { +template +int call(const char* name, Args... args) { Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); - auto func = reinterpret_cast(SIXENSE->resolve(name)); + auto func = reinterpret_cast(SIXENSE->resolve(name)); Q_ASSERT_X(func, __FUNCTION__, string("Could not resolve ").append(name).c_str()); - return func; + return func(args...); } // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); - auto sixenseInit = resolve("sixenseInit"); - return sixenseInit(); + return call(__FUNCTION__); } - int sixenseExit() { - auto sixenseExit = resolve("sixenseExit"); - auto returnCode = sixenseExit(); + auto returnCode = call(__FUNCTION__); unloadSixense(); return returnCode; } -int sixenseSetFilterEnabled(int input) { - auto sixenseSetFilterEnabled = resolve("sixenseSetFilterEnabled"); - return sixenseSetFilterEnabled(input); +int sixenseGetMaxBases() { + return call(__FUNCTION__); } - -int sixenseGetNumActiveControllers() { - auto sixenseGetNumActiveControllers = resolve("sixenseGetNumActiveControllers"); - return sixenseGetNumActiveControllers(); +int sixenseSetActiveBase(int i) { + return call(__FUNCTION__, i); +} +int sixenseIsBaseConnected(int i) { + return call(__FUNCTION__, i); } int sixenseGetMaxControllers() { - auto sixenseGetMaxControllers = resolve("sixenseGetMaxControllers"); - return sixenseGetMaxControllers(); + return call(__FUNCTION__); +} +int sixenseIsControllerEnabled(int which) { + return call(__FUNCTION__, which); +} +int sixenseGetNumActiveControllers() { + return call(__FUNCTION__); } -int sixenseIsControllerEnabled(int input) { - auto sixenseIsControllerEnabled = resolve("sixenseIsControllerEnabled"); - return sixenseIsControllerEnabled(input); +int sixenseGetHistorySize() { + return call(__FUNCTION__); } -int sixenseGetNewestData(int input1, sixenseControllerData* intput2) { - auto sixenseGetNewestData = resolve("sixenseGetNewestData"); - return sixenseGetNewestData(input1, intput2); +int sixenseGetData(int which, int index_back, sixenseControllerData* data) { + return call(__FUNCTION__, which, index_back, data); +} +int sixenseGetAllData(int index_back, sixenseAllControllerData* data) { + return call(__FUNCTION__, index_back, data); +} +int sixenseGetNewestData(int which, sixenseControllerData* data) { + return call(__FUNCTION__, which, data); +} +int sixenseGetAllNewestData(sixenseAllControllerData* data) { + return call(__FUNCTION__, data); +} + +int sixenseSetHemisphereTrackingMode(int which_controller, int state) { + return call(__FUNCTION__, which_controller, state); +} +int sixenseGetHemisphereTrackingMode(int which_controller, int* state) { + return call(__FUNCTION__, which_controller, state); +} +int sixenseAutoEnableHemisphereTracking(int which_controller) { + return call(__FUNCTION__, which_controller); +} + +int sixenseSetHighPriorityBindingEnabled(int on_or_off) { + return call(__FUNCTION__, on_or_off); +} +int sixenseGetHighPriorityBindingEnabled(int* on_or_off) { + return call(__FUNCTION__, on_or_off); +} + +int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) { + return call(__FUNCTION__, controller_id, duration_100ms, pattern_id); +} + +int sixenseSetFilterEnabled(int on_or_off) { + return call(__FUNCTION__, on_or_off); +} +int sixenseGetFilterEnabled(int* on_or_off) { + return call(__FUNCTION__, on_or_off); +} + +int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) { + return call(__FUNCTION__, near_range, near_val, far_range, far_val); +} +int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) { + return call(__FUNCTION__, near_range, near_val, far_range, far_val); +} + +int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) { + return call(__FUNCTION__, red, green, blue); +} +int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) { + return call(__FUNCTION__, red, green, blue); } #endif From 503e03d4a811b67c7beade04bc030c45a494cab3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 23:17:58 -0700 Subject: [PATCH 0764/1003] Remove unused `using` --- .../src/input-plugins/SixenseSupportOSX.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index a890d7c138..99af9c0ffc 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -16,13 +16,7 @@ #include #include -using std::string; -using std::unique_ptr; -using SixenseBaseFunction = int (*)(); -using SixenseTakeIntFunction = int (*)(int); -using SixenseTakeIntAndSixenseControllerData = int (*)(int, sixenseControllerData*); - -static unique_ptr SIXENSE; +static std::unique_ptr SIXENSE; void loadSixense() { if (!SIXENSE) { @@ -51,7 +45,7 @@ template int call(const char* name, Args... args) { Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); auto func = reinterpret_cast(SIXENSE->resolve(name)); - Q_ASSERT_X(func, __FUNCTION__, string("Could not resolve ").append(name).c_str()); + Q_ASSERT_X(func, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); return func(args...); } From e3b54f0272a25a8f38ddd4721109f4f1eae4a939 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 20 Oct 2015 23:41:20 -0700 Subject: [PATCH 0765/1003] Cleanup --- .../src/input-plugins/SixenseSupportOSX.cpp | 81 ++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 99af9c0ffc..4c8a285d76 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -1,6 +1,6 @@ // // SixenseSupportOSX.cpp -// +// libraries/input-plugins/src/input-plugins // // Created by Clement on 10/20/15. // Copyright 2015 High Fidelity, Inc. @@ -9,14 +9,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(HAVE_SIXENSE) #include "sixense.h" #include #include #include -static std::unique_ptr SIXENSE; +using Library = std::unique_ptr; +static Library SIXENSE; + +struct Callable { + template int operator() (Args... args){ + return reinterpret_cast(function)(args...); + } + QFunctionPointer function; +}; + +Callable resolve(const Library& library, const char* name) { + Q_ASSERT_X(library && library->isLoaded(), __FUNCTION__, "Sixense library not loaded"); + auto function = library->resolve(name); + Q_ASSERT_X(function, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); + return Callable { function }; +} +#define FOREWARD resolve(SIXENSE, __FUNCTION__) + void loadSixense() { if (!SIXENSE) { @@ -36,106 +53,98 @@ void loadSixense() { qDebug() << "Continuing without hydra support."; } } - void unloadSixense() { SIXENSE->unload(); } -template -int call(const char* name, Args... args) { - Q_ASSERT_X(SIXENSE && SIXENSE->isLoaded(), __FUNCTION__, "Sixense library not loaded"); - auto func = reinterpret_cast(SIXENSE->resolve(name)); - Q_ASSERT_X(func, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); - return func(args...); -} // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); - return call(__FUNCTION__); + return FOREWARD(); } int sixenseExit() { - auto returnCode = call(__FUNCTION__); + auto returnCode = FOREWARD(); unloadSixense(); return returnCode; } int sixenseGetMaxBases() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseSetActiveBase(int i) { - return call(__FUNCTION__, i); + return FOREWARD(i); } int sixenseIsBaseConnected(int i) { - return call(__FUNCTION__, i); + return FOREWARD(i); } int sixenseGetMaxControllers() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseIsControllerEnabled(int which) { - return call(__FUNCTION__, which); + return FOREWARD(which); } int sixenseGetNumActiveControllers() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseGetHistorySize() { - return call(__FUNCTION__); + return FOREWARD(); } int sixenseGetData(int which, int index_back, sixenseControllerData* data) { - return call(__FUNCTION__, which, index_back, data); + return FOREWARD(which, index_back, data); } int sixenseGetAllData(int index_back, sixenseAllControllerData* data) { - return call(__FUNCTION__, index_back, data); + return FOREWARD(index_back, data); } int sixenseGetNewestData(int which, sixenseControllerData* data) { - return call(__FUNCTION__, which, data); + return FOREWARD(which, data); } int sixenseGetAllNewestData(sixenseAllControllerData* data) { - return call(__FUNCTION__, data); + return FOREWARD(data); } int sixenseSetHemisphereTrackingMode(int which_controller, int state) { - return call(__FUNCTION__, which_controller, state); + return FOREWARD(which_controller, state); } int sixenseGetHemisphereTrackingMode(int which_controller, int* state) { - return call(__FUNCTION__, which_controller, state); + return FOREWARD(which_controller, state); } int sixenseAutoEnableHemisphereTracking(int which_controller) { - return call(__FUNCTION__, which_controller); + return FOREWARD(which_controller); } int sixenseSetHighPriorityBindingEnabled(int on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseGetHighPriorityBindingEnabled(int* on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) { - return call(__FUNCTION__, controller_id, duration_100ms, pattern_id); + return FOREWARD(controller_id, duration_100ms, pattern_id); } int sixenseSetFilterEnabled(int on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseGetFilterEnabled(int* on_or_off) { - return call(__FUNCTION__, on_or_off); + return FOREWARD(on_or_off); } int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) { - return call(__FUNCTION__, near_range, near_val, far_range, far_val); + return FOREWARD(near_range, near_val, far_range, far_val); } int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) { - return call(__FUNCTION__, near_range, near_val, far_range, far_val); + return FOREWARD(near_range, near_val, far_range, far_val); } int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) { - return call(__FUNCTION__, red, green, blue); + return FOREWARD(red, green, blue); } int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) { - return call(__FUNCTION__, red, green, blue); + return FOREWARD(red, green, blue); } #endif From 6ad20e417b41ddce22c7c13bc8d6811742b21600 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 21 Oct 2015 18:27:32 -0700 Subject: [PATCH 0766/1003] Typo --- .../src/input-plugins/SixenseSupportOSX.cpp | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 4c8a285d76..3ac2d197c2 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -32,7 +32,7 @@ Callable resolve(const Library& library, const char* name) { Q_ASSERT_X(function, __FUNCTION__, std::string("Could not resolve ").append(name).c_str()); return Callable { function }; } -#define FOREWARD resolve(SIXENSE, __FUNCTION__) +#define FORWARD resolve(SIXENSE, __FUNCTION__) void loadSixense() { @@ -61,90 +61,90 @@ void unloadSixense() { // sixense.h wrapper for OSX dynamic linking int sixenseInit() { loadSixense(); - return FOREWARD(); + return FORWARD(); } int sixenseExit() { - auto returnCode = FOREWARD(); + auto returnCode = FORWARD(); unloadSixense(); return returnCode; } int sixenseGetMaxBases() { - return FOREWARD(); + return FORWARD(); } int sixenseSetActiveBase(int i) { - return FOREWARD(i); + return FORWARD(i); } int sixenseIsBaseConnected(int i) { - return FOREWARD(i); + return FORWARD(i); } int sixenseGetMaxControllers() { - return FOREWARD(); + return FORWARD(); } int sixenseIsControllerEnabled(int which) { - return FOREWARD(which); + return FORWARD(which); } int sixenseGetNumActiveControllers() { - return FOREWARD(); + return FORWARD(); } int sixenseGetHistorySize() { - return FOREWARD(); + return FORWARD(); } int sixenseGetData(int which, int index_back, sixenseControllerData* data) { - return FOREWARD(which, index_back, data); + return FORWARD(which, index_back, data); } int sixenseGetAllData(int index_back, sixenseAllControllerData* data) { - return FOREWARD(index_back, data); + return FORWARD(index_back, data); } int sixenseGetNewestData(int which, sixenseControllerData* data) { - return FOREWARD(which, data); + return FORWARD(which, data); } int sixenseGetAllNewestData(sixenseAllControllerData* data) { - return FOREWARD(data); + return FORWARD(data); } int sixenseSetHemisphereTrackingMode(int which_controller, int state) { - return FOREWARD(which_controller, state); + return FORWARD(which_controller, state); } int sixenseGetHemisphereTrackingMode(int which_controller, int* state) { - return FOREWARD(which_controller, state); + return FORWARD(which_controller, state); } int sixenseAutoEnableHemisphereTracking(int which_controller) { - return FOREWARD(which_controller); + return FORWARD(which_controller); } int sixenseSetHighPriorityBindingEnabled(int on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseGetHighPriorityBindingEnabled(int* on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseTriggerVibration(int controller_id, int duration_100ms, int pattern_id) { - return FOREWARD(controller_id, duration_100ms, pattern_id); + return FORWARD(controller_id, duration_100ms, pattern_id); } int sixenseSetFilterEnabled(int on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseGetFilterEnabled(int* on_or_off) { - return FOREWARD(on_or_off); + return FORWARD(on_or_off); } int sixenseSetFilterParams(float near_range, float near_val, float far_range, float far_val) { - return FOREWARD(near_range, near_val, far_range, far_val); + return FORWARD(near_range, near_val, far_range, far_val); } int sixenseGetFilterParams(float* near_range, float* near_val, float* far_range, float* far_val) { - return FOREWARD(near_range, near_val, far_range, far_val); + return FORWARD(near_range, near_val, far_range, far_val); } int sixenseSetBaseColor(unsigned char red, unsigned char green, unsigned char blue) { - return FOREWARD(red, green, blue); + return FORWARD(red, green, blue); } int sixenseGetBaseColor(unsigned char* red, unsigned char* green, unsigned char* blue) { - return FOREWARD(red, green, blue); + return FORWARD(red, green, blue); } #endif From bc852d8eab14d6d6b05202e338470141c7197aff Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 6 Nov 2015 16:39:23 -0800 Subject: [PATCH 0767/1003] removing useless differences --- interface/resources/controllers/standard.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index c0e765dd77..6f1b2dbdff 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -14,6 +14,7 @@ ] }, { "from": "Standard.RX", "to": "Actions.Yaw" }, + { "from": "Standard.RY", "when": "Application.Grounded", "to": "Actions.Up", @@ -42,7 +43,3 @@ ] } - - - - From b1d5a36dc65ba07db594de79d40f0c063340aa2c Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 6 Nov 2015 16:40:46 -0800 Subject: [PATCH 0768/1003] removing useless differences --- interface/resources/controllers/standard.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 6f1b2dbdff..d4988fc00d 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -14,7 +14,7 @@ ] }, { "from": "Standard.RX", "to": "Actions.Yaw" }, - + { "from": "Standard.RY", "when": "Application.Grounded", "to": "Actions.Up", @@ -43,3 +43,4 @@ ] } + From 0a65bb145b0b4a1cb9445999619b576af93578ee Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 5 Nov 2015 11:08:20 -0800 Subject: [PATCH 0769/1003] Some more code cleanup --- interface/src/ui/ApplicationCompositor.cpp | 13 +------ .../src/input-plugins/SixenseManager.cpp | 38 +++++++++---------- .../src/input-plugins/SixenseManager.h | 23 +++-------- 3 files changed, 24 insertions(+), 50 deletions(-) diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 2a2a45b67b..5b7bbc2aba 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -473,18 +473,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { continue; } - int controllerButtons = 0; - - //Check for if we should toggle or drag the magnification window - if (controllerButtons & BUTTON_3) { - if (isPressed[index] == false) { - //We are now dragging the window - isPressed[index] = true; - //set the pressed time in us - pressedTime[index] = usecTimestampNow(); - stateWhenPressed[index] = _magActive[index]; - } - } else if (isPressed[index]) { + if (isPressed[index]) { isPressed[index] = false; //If the button was only pressed for < 250 ms //then disable it. diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 24b1b60356..56f79ce3c1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -24,21 +24,25 @@ Q_DECLARE_LOGGING_CATEGORY(inputplugins) Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") +#include #include #include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include "UserActivityLogger.h" +static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 +static const unsigned int BUTTON_1 = 1U << 5; +static const unsigned int BUTTON_2 = 1U << 6; +static const unsigned int BUTTON_3 = 1U << 3; +static const unsigned int BUTTON_4 = 1U << 4; +static const unsigned int BUTTON_FWD = 1U << 7; +static const unsigned int BUTTON_TRIGGER = 1U << 8; const glm::vec3 SixenseManager::DEFAULT_AVATAR_POSITION { -0.25f, -0.35f, -0.3f }; // in hydra frame const float SixenseManager::CONTROLLER_THRESHOLD { 0.35f }; -const float SixenseManager::DEFAULT_REACH_LENGTH { 1.5f }; const QString SixenseManager::NAME = "Sixense"; const QString SixenseManager::HYDRA_ID_STRING = "Razer Hydra"; @@ -47,7 +51,6 @@ const QString MENU_PARENT = "Avatar"; const QString MENU_NAME = "Sixense"; const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; -const float DEFAULT_REACH_LENGTH = 1.5f; bool SixenseManager::isSupported() const { #ifdef HAVE_SIXENSE @@ -65,11 +68,11 @@ bool SixenseManager::isSupported() const { void SixenseManager::activate() { InputPlugin::activate(); + #ifdef HAVE_SIXENSE - _container->addMenu(MENU_PATH); _container->addMenuItem(MENU_PATH, TOGGLE_SMOOTH, - [this] (bool clicked) { this->setSixenseFilter(clicked); }, + [this] (bool clicked) { setSixenseFilter(clicked); }, true, true); auto userInputMapper = DependencyManager::get(); @@ -96,6 +99,7 @@ void SixenseManager::deactivate() { } sixenseExit(); + saveSettings(); #endif } @@ -123,8 +127,6 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); - auto userInputMapper = DependencyManager::get(); - static const float MAX_DISCONNECTED_TIME = 2.0f; static bool disconnected { false }; static float disconnectedInterval { 0.0f }; @@ -218,9 +220,9 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { // (4) assume that the orb is on a flat surface (yAxis is UP) // (5) compute the forward direction (zAxis = xAxis cross yAxis) -const float MINIMUM_ARM_REACH = 0.3f; // meters -const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters -const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired +static const float MINIMUM_ARM_REACH = 0.3f; // meters +static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters +static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired void SixenseManager::InputDevice::updateCalibration(void* controllersX) { auto controllers = reinterpret_cast(controllersX); @@ -240,14 +242,12 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) { glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); - _reachLength = glm::dot(xAxis, _reachRight - _reachLeft); _avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis))); const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; qCDebug(inputplugins, "succeess: sixense calibration"); } break; - default: _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); @@ -441,8 +441,6 @@ static const auto R2 = controller::A; static const auto R3 = controller::B; static const auto R4 = controller::Y; -using namespace controller; - controller::Input::NamedVector SixenseManager::InputDevice::getAvailableInputs() const { using namespace controller; static const Input::NamedVector availableInputs { @@ -486,7 +484,6 @@ void SixenseManager::saveSettings() const { { settings.setVec3Value(QString("avatarPosition"), _inputDevice->_avatarPosition); settings.setQuatValue(QString("avatarRotation"), _inputDevice->_avatarRotation); - settings.setValue(QString("reachLength"), QVariant(_inputDevice->_reachLength)); } settings.endGroup(); } @@ -498,7 +495,6 @@ void SixenseManager::loadSettings() { { settings.getVec3ValueIfValid(QString("avatarPosition"), _inputDevice->_avatarPosition); settings.getQuatValueIfValid(QString("avatarRotation"), _inputDevice->_avatarRotation); - settings.getFloatValueIfValid(QString("reachLength"), _inputDevice->_reachLength); } settings.endGroup(); } diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index bf657d347b..bdfbd539cb 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -19,21 +19,10 @@ #include "InputPlugin.h" -const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 -const unsigned int BUTTON_1 = 1U << 5; -const unsigned int BUTTON_2 = 1U << 6; -const unsigned int BUTTON_3 = 1U << 3; -const unsigned int BUTTON_4 = 1U << 4; -const unsigned int BUTTON_FWD = 1U << 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 { Q_OBJECT public: - // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } @@ -52,17 +41,18 @@ public: public slots: void setSixenseFilter(bool filter); -private: +private: static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s static const int CALIBRATION_STATE_IDLE = 0; static const int CALIBRATION_STATE_IN_PROGRESS = 1; static const int CALIBRATION_STATE_COMPLETE = 2; static const glm::vec3 DEFAULT_AVATAR_POSITION; static const float CONTROLLER_THRESHOLD; - static const float DEFAULT_REACH_LENGTH; - - using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; - using MovingAverageMap = std::map< int, Samples >; + + template + using SampleAverage = MovingAverage; + using Samples = std::pair, SampleAverage>; + using MovingAverageMap = std::map; class InputDevice : public controller::InputDevice { public: @@ -88,7 +78,6 @@ private: glm::vec3 _avatarPosition { DEFAULT_AVATAR_POSITION }; // in hydra-frame glm::quat _avatarRotation; // in hydra-frame - float _reachLength { DEFAULT_REACH_LENGTH }; float _lastDistance; // these are measured values used to compute the calibration results quint64 _lockExpiry; From d95709ee731aaa9fa031963e021b10d32062ddfc Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 16:45:23 -0800 Subject: [PATCH 0770/1003] Added input plugins logging category --- .../src/input-plugins/InputPluginsLogging.cpp | 14 ++++++++++++++ .../src/input-plugins/InputPluginsLogging.h | 18 ++++++++++++++++++ .../src/input-plugins/SixenseManager.cpp | 5 ----- .../src/input-plugins/SixenseSupportOSX.cpp | 2 ++ 4 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp create mode 100644 libraries/input-plugins/src/input-plugins/InputPluginsLogging.h diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp new file mode 100644 index 0000000000..c96941a8ca --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp @@ -0,0 +1,14 @@ +// +// InputPluginsLogging.cpp +// +// +// Created by Clement on 11/6/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 "InputPluginsLogging.h" + +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h new file mode 100644 index 0000000000..6460469190 --- /dev/null +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h @@ -0,0 +1,18 @@ +// +// InputPluginsLogging.h +// +// +// Created by Clement on 11/6/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 +// + +#ifndef hifi_InputPluginsLogging_h +#define hifi_InputPluginsLogging_h + +#include +Q_DECLARE_LOGGING_CATEGORY(inputplugins) + +#endif // hifi_InputPluginsLogging_h diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 56f79ce3c1..9340cbf950 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -19,11 +19,6 @@ #include #include -// TODO: This should not be here -#include -Q_DECLARE_LOGGING_CATEGORY(inputplugins) -Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") - #include #include #include diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 3ac2d197c2..4a4eab2ee1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -16,6 +16,8 @@ #include #include +#include "InputPluginsLogging.h" + using Library = std::unique_ptr; static Library SIXENSE; From 6323728d7a5092073ec445663ff2d8ea276ad8b1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 16:56:48 -0800 Subject: [PATCH 0771/1003] Added perfect forwarding of arguments --- .../src/input-plugins/SixenseManager.cpp | 4 +++- .../src/input-plugins/SixenseSupportOSX.cpp | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 9340cbf950..3085b377f0 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -12,7 +12,7 @@ #include "SixenseManager.h" #ifdef HAVE_SIXENSE -#include "sixense.h" +#include #endif #include @@ -28,6 +28,8 @@ #include #include +#include "InputPluginsLogging.h" + static const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 static const unsigned int BUTTON_1 = 1U << 5; static const unsigned int BUTTON_2 = 1U << 6; diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 4a4eab2ee1..378c99de76 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -9,11 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Mock implementation of sixense.h to hide dynamic linking on OS X #if defined(__APPLE__) && defined(HAVE_SIXENSE) -#include "sixense.h" +#include + +#include #include -#include #include #include "InputPluginsLogging.h" @@ -22,8 +24,9 @@ using Library = std::unique_ptr; static Library SIXENSE; struct Callable { - template int operator() (Args... args){ - return reinterpret_cast(function)(args...); + template + int operator() (Args&&... args){ + return reinterpret_cast(function)(std::forward(args)...); } QFunctionPointer function; }; From 0c60538952e82a3b0ce5afe338ca490ec88048ee Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 17:47:02 -0800 Subject: [PATCH 0772/1003] Ensure that dependencies of runtime plugins are included in the DLL copy --- cmake/macros/CopyDllsBesideWindowsExecutable.cmake | 7 +++++++ cmake/templates/FixupBundlePostBuild.cmake.in | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake b/cmake/macros/CopyDllsBesideWindowsExecutable.cmake index b57c781eff..69fd20a57b 100644 --- a/cmake/macros/CopyDllsBesideWindowsExecutable.cmake +++ b/cmake/macros/CopyDllsBesideWindowsExecutable.cmake @@ -18,12 +18,19 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE) @ONLY ) + if (APPLE) + set(PLUGIN_PATH "interface.app/Contents/MacOS/plugins") + else() + set(PLUGIN_PATH "plugins") + endif() + # add a post-build command to copy DLLs beside the executable add_custom_command( TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -DBUNDLE_EXECUTABLE=$ + -DBUNDLE_PLUGIN_DIR=$/${PLUGIN_PATH} -P ${CMAKE_CURRENT_BINARY_DIR}/FixupBundlePostBuild.cmake ) diff --git a/cmake/templates/FixupBundlePostBuild.cmake.in b/cmake/templates/FixupBundlePostBuild.cmake.in index 4afe4de403..ee686a6967 100644 --- a/cmake/templates/FixupBundlePostBuild.cmake.in +++ b/cmake/templates/FixupBundlePostBuild.cmake.in @@ -41,4 +41,16 @@ function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item) endfunction() message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@") -fixup_bundle("${BUNDLE_EXECUTABLE}" "" "@FIXUP_LIBS@") \ No newline at end of file + +message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}") + +if (APPLE) + set(PLUGIN_EXTENSION "dylib") +elseif (WIN32) + set(PLUGIN_EXTENSION "dll") +else() + set(PLUGIN_EXTENSION "so") +endif() + +file(GLOB RUNTIME_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}") +fixup_bundle("${BUNDLE_EXECUTABLE}" "${RUNTIME_PLUGINS}" "@FIXUP_LIBS@") \ No newline at end of file From 11733c039f5311c09de192e76d286cd9298f7269 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 2 Nov 2015 17:16:28 -0800 Subject: [PATCH 0773/1003] Allow shared libraries to access core global objects --- interface/src/Menu.cpp | 17 +++---------- interface/src/Menu.h | 4 +--- libraries/shared/src/DependencyManager.cpp | 9 ++++++- libraries/shared/src/DependencyManager.h | 22 ++++++++--------- libraries/shared/src/SharedUtil.h | 28 +++++++++++++++++++++- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 92ff39a489..2d60b9d79f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -44,22 +44,11 @@ #include "Menu.h" -Menu* Menu::_instance = NULL; +static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; Menu* Menu::getInstance() { - static QMutex menuInstanceMutex; - - // lock the menu instance mutex to make sure we don't race and create two menus and crash - menuInstanceMutex.lock(); - - if (!_instance) { - qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); - _instance = new Menu(); - } - - menuInstanceMutex.unlock(); - - return _instance; + static Menu& instance = globalInstace(MENU_PROPERTY_NAME); + return &instance; } Menu::Menu() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 162fad1b9f..dfa2cfa41b 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -57,6 +57,7 @@ private: class Menu : public QMenuBar { Q_OBJECT public: + Menu(); static Menu* getInstance(); void loadSettings(); @@ -103,9 +104,6 @@ public slots: void setIsOptionChecked(const QString& menuOption, bool isChecked); private: - static Menu* _instance; - Menu(); - typedef void(*settingsAction)(Settings&, QAction&); static void loadAction(Settings& settings, QAction& action); static void saveAction(Settings& settings, QAction& action); diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index 5f78f6bcd5..54d4e08259 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -11,7 +11,14 @@ #include "DependencyManager.h" -DependencyManager DependencyManager::_manager; +#include "SharedUtil.h" + +static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger"; + +DependencyManager& DependencyManager::manager() { + static DependencyManager& instance = globalInstace(DEPENDENCY_PROPERTY_NAME); + return instance; +} QSharedPointer& DependencyManager::safeGet(size_t hashCode) { return _instanceHash[hashCode]; diff --git a/libraries/shared/src/DependencyManager.h b/libraries/shared/src/DependencyManager.h index db41e72e1e..c0568bc752 100644 --- a/libraries/shared/src/DependencyManager.h +++ b/libraries/shared/src/DependencyManager.h @@ -62,8 +62,8 @@ public: static void registerInheritance(); private: - static DependencyManager _manager; - + static DependencyManager& manager(); + template size_t getHashCode(); @@ -75,11 +75,11 @@ private: template QSharedPointer DependencyManager::get() { - static size_t hashCode = _manager.getHashCode(); + static size_t hashCode = manager().getHashCode(); static QWeakPointer instance; if (instance.isNull()) { - instance = qSharedPointerCast(_manager.safeGet(hashCode)); + instance = qSharedPointerCast(manager().safeGet(hashCode)); if (instance.isNull()) { qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name(); @@ -91,9 +91,9 @@ QSharedPointer DependencyManager::get() { template QSharedPointer DependencyManager::set(Args&&... args) { - static size_t hashCode = _manager.getHashCode(); + static size_t hashCode = manager().getHashCode(); - QSharedPointer& instance = _manager.safeGet(hashCode); + 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); @@ -104,9 +104,9 @@ QSharedPointer DependencyManager::set(Args&&... args) { template QSharedPointer DependencyManager::set(Args&&... args) { - static size_t hashCode = _manager.getHashCode(); + static size_t hashCode = manager().getHashCode(); - QSharedPointer& instance = _manager.safeGet(hashCode); + 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); @@ -117,15 +117,15 @@ QSharedPointer DependencyManager::set(Args&&... args) { template void DependencyManager::destroy() { - static size_t hashCode = _manager.getHashCode(); - _manager.safeGet(hashCode).clear(); + static size_t hashCode = manager().getHashCode(); + manager().safeGet(hashCode).clear(); } template void DependencyManager::registerInheritance() { size_t baseHashCode = typeid(Base).hash_code(); size_t derivedHashCode = typeid(Derived).hash_code(); - _manager._inheritanceHash.insert(baseHashCode, derivedHashCode); + manager()._inheritanceHash.insert(baseHashCode, derivedHashCode); } template diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index cd4f734d40..4c547417c8 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -13,6 +13,7 @@ #define hifi_SharedUtil_h #include +#include #include #include @@ -20,7 +21,32 @@ #include // not on windows, not needed for mac or windows #endif -#include +#include +#include + +// Provides efficient access to a named global type. By storing the value +// in the QApplication by name we can implement the singleton pattern and +// have the single instance function across DLL boundaries. +template +T& globalInstace(const char* propertyName, Args&&... args) { + static T *instance { nullptr }; + static std::mutex mutex; + if (!instance) { + std::unique_lock lock(mutex); + if (!instance) { + auto variant = qApp->property(propertyName); + if (variant.isNull()) { + auto* typedInstance = new T(args...); + void* voidInstance = typedInstance; + variant = QVariant::fromValue(voidInstance); + qApp->setProperty(propertyName, variant); + } + void* returnedVoidInstance = variant.value(); + instance = static_cast(returnedVoidInstance); + } + } + return *instance; +} const int BYTES_PER_COLOR = 3; const int BYTES_PER_FLAGS = 1; From 37a9538f104b6efec4b7cfc34f9474c6ba746be4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 4 Nov 2015 19:10:31 -0800 Subject: [PATCH 0774/1003] PR feedback, ensure destruction of shared objects --- interface/src/Menu.cpp | 4 ++-- libraries/shared/src/DependencyManager.cpp | 5 +++-- libraries/shared/src/SharedUtil.h | 19 +++++++++++-------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2d60b9d79f..3ed31e609a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -47,8 +47,8 @@ static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; Menu* Menu::getInstance() { - static Menu& instance = globalInstace(MENU_PROPERTY_NAME); - return &instance; + static Menu* instance = globalInstace(MENU_PROPERTY_NAME); + return instance; } Menu::Menu() { diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index 54d4e08259..5100d99691 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -12,12 +12,13 @@ #include "DependencyManager.h" #include "SharedUtil.h" +#include "Finally.h" static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger"; DependencyManager& DependencyManager::manager() { - static DependencyManager& instance = globalInstace(DEPENDENCY_PROPERTY_NAME); - return instance; + static DependencyManager* instance = globalInstace(DEPENDENCY_PROPERTY_NAME); + return *instance; } QSharedPointer& DependencyManager::safeGet(size_t hashCode) { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 4c547417c8..097444a62c 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -28,24 +28,27 @@ // in the QApplication by name we can implement the singleton pattern and // have the single instance function across DLL boundaries. template -T& globalInstace(const char* propertyName, Args&&... args) { - static T *instance { nullptr }; +T* globalInstace(const char* propertyName, Args&&... args) { + static std::shared_ptr instancePtr; + static T *resultInstance { nullptr }; static std::mutex mutex; - if (!instance) { + if (!resultInstance) { std::unique_lock lock(mutex); - if (!instance) { + if (!resultInstance) { auto variant = qApp->property(propertyName); if (variant.isNull()) { - auto* typedInstance = new T(args...); - void* voidInstance = typedInstance; + // Since we're building the object, store it in a shared_ptr so it's + // destroyed by the destructor of the static instancePtr + instancePtr = std::make_shared(args...); + void* voidInstance = &(*instancePtr); variant = QVariant::fromValue(voidInstance); qApp->setProperty(propertyName, variant); } void* returnedVoidInstance = variant.value(); - instance = static_cast(returnedVoidInstance); + resultInstance = static_cast(returnedVoidInstance); } } - return *instance; + return resultInstance; } const int BYTES_PER_COLOR = 3; From 314c11e8d42760d6afde193410bfcd5ce2501096 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 6 Nov 2015 16:59:46 -0800 Subject: [PATCH 0775/1003] Make life easier for Sam. --- libraries/animation/src/Rig.cpp | 4 ++++ libraries/avatars/src/AvatarData.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4d66097265..7926b268b5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -300,6 +300,8 @@ void Rig::setJointAnimatinoPriority(int index, float newPriority) { } } +// Deprecated. +// WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; @@ -350,6 +352,8 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const return true; } +// Deprecated. +// WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return false; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3abd63bf63..9079f15f53 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -240,7 +240,8 @@ public: Q_INVOKABLE void setHandState(char s) { _handState = s; } Q_INVOKABLE char getHandState() const { return _handState; } - const QVector& getJointData() const { return _jointData; } + const QVector& getRawJointData() const { return _jointData; } + void setRawJointData(QVector data) { _jointData = data; } Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); From d8f169bd4e11c535a70fa6df6b01760f1ccf105e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 17:04:55 -0800 Subject: [PATCH 0776/1003] add collision sounds to bballs --- unpublishedScripts/basketballsResetter.js | 2 +- unpublishedScripts/hiddenEntityReset.js | 3 ++- unpublishedScripts/masterReset.js | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/unpublishedScripts/basketballsResetter.js b/unpublishedScripts/basketballsResetter.js index 4d02296e8e..791ac1c0cf 100644 --- a/unpublishedScripts/basketballsResetter.js +++ b/unpublishedScripts/basketballsResetter.js @@ -82,7 +82,7 @@ var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; z: 0 }, collisionsWillMove: true, - collisionsSoundURL: basketballCollisionSoundURL, + collisionSoundURL: basketballCollisionSoundURL, ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index ef0557a54f..0773cc5640 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -308,6 +308,7 @@ z: 0 }, collisionsWillMove: true, + collisionSoundURL: 'http://hifi-public.s3.amazonaws.com/sounds/basketball/basketball.wav', ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ @@ -1258,4 +1259,4 @@ }; // entity scripts always need to return a newly constructed object of our type return new ResetSwitch(); -}); +}); \ No newline at end of file diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index a8e156617a..aa7a1912de 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -248,7 +248,7 @@ MasterReset = function() { }, grabbableKey: { grabbable: false, - wantsTrigger:true + wantsTrigger: true } }) }); @@ -289,6 +289,7 @@ MasterReset = function() { z: 0 }, collisionsWillMove: true, + collisionSoundURL: 'http://hifi-public.s3.amazonaws.com/sounds/basketball/basketball.wav', ignoreForCollisions: false, modelURL: basketballURL, userData: JSON.stringify({ @@ -334,7 +335,7 @@ MasterReset = function() { name: "Basketball Resetter", script: basketballResetterScriptURL, dimensions: dimensions, - visible:false, + visible: false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -367,7 +368,7 @@ MasterReset = function() { name: "Target Resetter", script: targetsResetterScriptURL, dimensions: dimensions, - visible:false, + visible: false, userData: JSON.stringify({ resetMe: { resetMe: true @@ -1238,4 +1239,4 @@ MasterReset = function() { Script.scriptEnding.connect(cleanup); } -}; +}; \ No newline at end of file From 137a2c1c48bfe67d5f4713342297cf1ce1176a07 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 17:15:34 -0800 Subject: [PATCH 0777/1003] Fix file headers --- .../input-plugins/src/input-plugins/InputPluginsLogging.cpp | 2 +- libraries/input-plugins/src/input-plugins/InputPluginsLogging.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp index c96941a8ca..43a708e5c7 100644 --- a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.cpp @@ -1,6 +1,6 @@ // // InputPluginsLogging.cpp -// +// libraries/input-plugins/src/input-plugins // // Created by Clement on 11/6/15. // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h index 6460469190..f82ffdbe2e 100644 --- a/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h +++ b/libraries/input-plugins/src/input-plugins/InputPluginsLogging.h @@ -1,6 +1,6 @@ // // InputPluginsLogging.h -// +// libraries/input-plugins/src/input-plugins // // Created by Clement on 11/6/15. // Copyright 2015 High Fidelity, Inc. From 6122fa145db9beadb66581223c80a48cc11901fc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 6 Nov 2015 17:02:56 -0800 Subject: [PATCH 0778/1003] More PR feedback --- interface/src/Menu.cpp | 2 +- libraries/shared/src/DependencyManager.cpp | 4 ++-- libraries/shared/src/SharedUtil.h | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3ed31e609a..24033325f6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -47,7 +47,7 @@ static const char* const MENU_PROPERTY_NAME = "com.highfidelity.Menu"; Menu* Menu::getInstance() { - static Menu* instance = globalInstace(MENU_PROPERTY_NAME); + static Menu* instance = globalInstance(MENU_PROPERTY_NAME); return instance; } diff --git a/libraries/shared/src/DependencyManager.cpp b/libraries/shared/src/DependencyManager.cpp index 5100d99691..a870feab98 100644 --- a/libraries/shared/src/DependencyManager.cpp +++ b/libraries/shared/src/DependencyManager.cpp @@ -17,10 +17,10 @@ static const char* const DEPENDENCY_PROPERTY_NAME = "com.highfidelity.DependencyMananger"; DependencyManager& DependencyManager::manager() { - static DependencyManager* instance = globalInstace(DEPENDENCY_PROPERTY_NAME); + static DependencyManager* instance = globalInstance(DEPENDENCY_PROPERTY_NAME); return *instance; } QSharedPointer& DependencyManager::safeGet(size_t hashCode) { return _instanceHash[hashCode]; -} \ No newline at end of file +} diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 097444a62c..89ccba1479 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -27,10 +27,10 @@ // Provides efficient access to a named global type. By storing the value // in the QApplication by name we can implement the singleton pattern and // have the single instance function across DLL boundaries. -template -T* globalInstace(const char* propertyName, Args&&... args) { - static std::shared_ptr instancePtr; - static T *resultInstance { nullptr }; +template +T* globalInstance(const char* propertyName, Args&&... args) { + static std::unique_ptr instancePtr; + static T* resultInstance { nullptr }; static std::mutex mutex; if (!resultInstance) { std::unique_lock lock(mutex); @@ -39,7 +39,8 @@ T* globalInstace(const char* propertyName, Args&&... args) { if (variant.isNull()) { // Since we're building the object, store it in a shared_ptr so it's // destroyed by the destructor of the static instancePtr - instancePtr = std::make_shared(args...); + instancePtr = std::unique_ptr(new T(std::forward(args)...)); + void* voidInstance = &(*instancePtr); variant = QVariant::fromValue(voidInstance); qApp->setProperty(propertyName, variant); From cbeb56aba4e99df963bdfdc8eb3ec5b716463688 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 6 Nov 2015 17:25:50 -0800 Subject: [PATCH 0779/1003] using the frameInterpolatorCOrrectly --- libraries/avatars/src/Player.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 485f8772ff..d3a7124226 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -246,21 +246,19 @@ void Player::play() { nextFrame.getScale(), _frameInterpolationFactor); _avatar->setTargetScale(context->scale * scale); - - float animFactor = 0.0f; QVector jointRotations(currentFrame.getJointRotations().size()); for (int i = 0; i < currentFrame.getJointRotations().size(); ++i) { jointRotations[i] = safeMix(currentFrame.getJointRotations()[i], nextFrame.getJointRotations()[i], - animFactor); + _frameInterpolationFactor); } QVector jointTranslations(currentFrame.getJointTranslations().size()); for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) { jointTranslations[i] = glm::mix(currentFrame.getJointTranslations()[i], nextFrame.getJointTranslations()[i], - animFactor); + _frameInterpolationFactor); } _avatar->setJointRotations(jointRotations); From 68a72b0f4161ea565bbd5f2a4d19efb74aecfbcd Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 6 Nov 2015 17:22:12 -0800 Subject: [PATCH 0780/1003] Bit of cleanup --- .../src/input-plugins/SixenseSupportOSX.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp index 378c99de76..f6cec5d67f 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseSupportOSX.cpp @@ -20,6 +20,10 @@ #include "InputPluginsLogging.h" +#ifndef SIXENSE_LIB_FILENAME +#define SIXENSE_LIB_FILENAME QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64" +#endif + using Library = std::unique_ptr; static Library SIXENSE; @@ -41,15 +45,9 @@ Callable resolve(const Library& library, const char* name) { void loadSixense() { - if (!SIXENSE) { - static const QString LIBRARY_PATH = -#ifdef SIXENSE_LIB_FILENAME - SIXENSE_LIB_FILENAME; -#else - QCoreApplication::applicationDirPath() + "/../Frameworks/libsixense_x64"; -#endif - SIXENSE.reset(new QLibrary(LIBRARY_PATH)); - } + Q_ASSERT_X(!(SIXENSE && SIXENSE->isLoaded()), __FUNCTION__, "Sixense library already loaded"); + SIXENSE.reset(new QLibrary(SIXENSE_LIB_FILENAME)); + Q_CHECK_PTR(SIXENSE); if (SIXENSE->load()){ qDebug() << "Loaded sixense library for hydra support -" << SIXENSE->fileName(); From c4318bcbd3c7aecab58b1b49ed5db92981504bc0 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 17:38:53 -0800 Subject: [PATCH 0781/1003] add easystar lib --- examples/libraries/easyStar.js | 744 ++++++++++++++++++++++++++ examples/libraries/easyStarExample.js | 30 ++ 2 files changed, 774 insertions(+) create mode 100644 examples/libraries/easyStar.js create mode 100644 examples/libraries/easyStarExample.js diff --git a/examples/libraries/easyStar.js b/examples/libraries/easyStar.js new file mode 100644 index 0000000000..b76ff1d7ec --- /dev/null +++ b/examples/libraries/easyStar.js @@ -0,0 +1,744 @@ +// The MIT License (MIT) + +// Copyright (c) 2012-2015 Bryce Neal + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Adapted for High Fidelity by James B. Pollack on 11/6/2015 + +loadEasyStar = function() { + var ezStar = eStar(); + return new ezStar.js() +} + +var eStar = function() { + + var EasyStar = EasyStar || {}; + + + /** + * A simple Node that represents a single tile on the grid. + * @param {Object} parent The parent node. + * @param {Number} x The x position on the grid. + * @param {Number} y The y position on the grid. + * @param {Number} costSoFar How far this node is in moves*cost from the start. + * @param {Number} simpleDistanceToTarget Manhatten distance to the end point. + **/ + EasyStar.Node = function(parent, x, y, costSoFar, simpleDistanceToTarget) { + this.parent = parent; + this.x = x; + this.y = y; + this.costSoFar = costSoFar; + this.simpleDistanceToTarget = simpleDistanceToTarget; + + /** + * @return {Number} Best guess distance of a cost using this node. + **/ + this.bestGuessDistance = function() { + return this.costSoFar + this.simpleDistanceToTarget; + } + }; + + // Constants + EasyStar.Node.OPEN_LIST = 0; + EasyStar.Node.CLOSED_LIST = 1; + /** + * This is an improved Priority Queue data type implementation that can be used to sort any object type. + * It uses a technique called a binary heap. + * + * For more on binary heaps see: http://en.wikipedia.org/wiki/Binary_heap + * + * @param {String} criteria The criteria by which to sort the objects. + * This should be a property of the objects you're sorting. + * + * @param {Number} heapType either PriorityQueue.MAX_HEAP or PriorityQueue.MIN_HEAP. + **/ + EasyStar.PriorityQueue = function(criteria, heapType) { + this.length = 0; //The current length of heap. + var queue = []; + var isMax = false; + + //Constructor + if (heapType == EasyStar.PriorityQueue.MAX_HEAP) { + isMax = true; + } else if (heapType == EasyStar.PriorityQueue.MIN_HEAP) { + isMax = false; + } else { + throw heapType + " not supported."; + } + + /** + * Inserts the value into the heap and sorts it. + * + * @param value The object to insert into the heap. + **/ + this.insert = function(value) { + if (!value.hasOwnProperty(criteria)) { + throw "Cannot insert " + value + " because it does not have a property by the name of " + criteria + "."; + } + queue.push(value); + this.length++; + bubbleUp(this.length - 1); + } + + /** + * Peeks at the highest priority element. + * + * @return the highest priority element + **/ + this.getHighestPriorityElement = function() { + return queue[0]; + } + + /** + * Removes and returns the highest priority element from the queue. + * + * @return the highest priority element + **/ + this.shiftHighestPriorityElement = function() { + if (this.length === 0) { + throw ("There are no more elements in your priority queue."); + } else if (this.length === 1) { + var onlyValue = queue[0]; + queue = []; + this.length = 0; + return onlyValue; + } + var oldRoot = queue[0]; + var newRoot = queue.pop(); + this.length--; + queue[0] = newRoot; + swapUntilQueueIsCorrect(0); + return oldRoot; + } + + var bubbleUp = function(index) { + if (index === 0) { + return; + } + var parent = getParentOf(index); + if (evaluate(index, parent)) { + swap(index, parent); + bubbleUp(parent); + } else { + return; + } + } + + var swapUntilQueueIsCorrect = function(value) { + var left = getLeftOf(value); + var right = getRightOf(value); + if (evaluate(left, value)) { + swap(value, left); + swapUntilQueueIsCorrect(left); + } else if (evaluate(right, value)) { + swap(value, right); + swapUntilQueueIsCorrect(right); + } else if (value == 0) { + return; + } else { + swapUntilQueueIsCorrect(0); + } + } + + var swap = function(self, target) { + var placeHolder = queue[self]; + queue[self] = queue[target]; + queue[target] = placeHolder; + } + + var evaluate = function(self, target) { + if (queue[target] === undefined || queue[self] === undefined) { + return false; + } + + var selfValue; + var targetValue; + + // Check if the criteria should be the result of a function call. + if (typeof queue[self][criteria] === 'function') { + selfValue = queue[self][criteria](); + targetValue = queue[target][criteria](); + } else { + selfValue = queue[self][criteria]; + targetValue = queue[target][criteria]; + } + + if (isMax) { + if (selfValue > targetValue) { + return true; + } else { + return false; + } + } else { + if (selfValue < targetValue) { + return true; + } else { + return false; + } + } + } + + var getParentOf = function(index) { + return Math.floor((index - 1) / 2); + } + + var getLeftOf = function(index) { + return index * 2 + 1; + } + + var getRightOf = function(index) { + return index * 2 + 2; + } + }; + + // Constants + EasyStar.PriorityQueue.MAX_HEAP = 0; + EasyStar.PriorityQueue.MIN_HEAP = 1; + + /** + * Represents a single instance of EasyStar. + * A path that is in the queue to eventually be found. + */ + EasyStar.instance = function() { + this.isDoneCalculating = true; + this.pointsToAvoid = {}; + this.startX; + this.callback; + this.startY; + this.endX; + this.endY; + this.nodeHash = {}; + this.openList; + }; + /** + * EasyStar.js + * github.com/prettymuchbryce/EasyStarJS + * Licensed under the MIT license. + * + * Implementation By Bryce Neal (@prettymuchbryce) + **/ + EasyStar.js = function() { + var STRAIGHT_COST = 1.0; + var DIAGONAL_COST = 1.4; + var syncEnabled = false; + var pointsToAvoid = {}; + var collisionGrid; + var costMap = {}; + var pointsToCost = {}; + var allowCornerCutting = true; + var iterationsSoFar; + var instances = []; + var iterationsPerCalculation = Number.MAX_VALUE; + var acceptableTiles; + var diagonalsEnabled = false; + + /** + * Sets the collision grid that EasyStar uses. + * + * @param {Array|Number} tiles An array of numbers that represent + * which tiles in your grid should be considered + * acceptable, or "walkable". + **/ + this.setAcceptableTiles = function(tiles) { + if (tiles instanceof Array) { + // Array + acceptableTiles = tiles; + } else if (!isNaN(parseFloat(tiles)) && isFinite(tiles)) { + // Number + acceptableTiles = [tiles]; + } + }; + + /** + * Enables sync mode for this EasyStar instance.. + * if you're into that sort of thing. + **/ + this.enableSync = function() { + syncEnabled = true; + }; + + /** + * Disables sync mode for this EasyStar instance. + **/ + this.disableSync = function() { + syncEnabled = false; + }; + + /** + * Enable diagonal pathfinding. + */ + this.enableDiagonals = function() { + diagonalsEnabled = true; + } + + /** + * Disable diagonal pathfinding. + */ + this.disableDiagonals = function() { + diagonalsEnabled = false; + } + + /** + * Sets the collision grid that EasyStar uses. + * + * @param {Array} grid The collision grid that this EasyStar instance will read from. + * This should be a 2D Array of Numbers. + **/ + this.setGrid = function(grid) { + print('EASY GRID') + collisionGrid = grid; + + //Setup cost map + for (var y = 0; y < collisionGrid.length; y++) { + for (var x = 0; x < collisionGrid[0].length; x++) { + if (!costMap[collisionGrid[y][x]]) { + costMap[collisionGrid[y][x]] = 1 + } + } + } + }; + + /** + * Sets the tile cost for a particular tile type. + * + * @param {Number} The tile type to set the cost for. + * @param {Number} The multiplicative cost associated with the given tile. + **/ + this.setTileCost = function(tileType, cost) { + costMap[tileType] = cost; + }; + + /** + * Sets the an additional cost for a particular point. + * Overrides the cost from setTileCost. + * + * @param {Number} x The x value of the point to cost. + * @param {Number} y The y value of the point to cost. + * @param {Number} The multiplicative cost associated with the given point. + **/ + this.setAdditionalPointCost = function(x, y, cost) { + pointsToCost[x + '_' + y] = cost; + }; + + /** + * Remove the additional cost for a particular point. + * + * @param {Number} x The x value of the point to stop costing. + * @param {Number} y The y value of the point to stop costing. + **/ + this.removeAdditionalPointCost = function(x, y) { + delete pointsToCost[x + '_' + y]; + } + + /** + * Remove all additional point costs. + **/ + this.removeAllAdditionalPointCosts = function() { + pointsToCost = {}; + } + + /** + * Sets the number of search iterations per calculation. + * A lower number provides a slower result, but more practical if you + * have a large tile-map and don't want to block your thread while + * finding a path. + * + * @param {Number} iterations The number of searches to prefrom per calculate() call. + **/ + this.setIterationsPerCalculation = function(iterations) { + iterationsPerCalculation = iterations; + }; + + /** + * Avoid a particular point on the grid, + * regardless of whether or not it is an acceptable tile. + * + * @param {Number} x The x value of the point to avoid. + * @param {Number} y The y value of the point to avoid. + **/ + this.avoidAdditionalPoint = function(x, y) { + pointsToAvoid[x + "_" + y] = 1; + }; + + /** + * Stop avoiding a particular point on the grid. + * + * @param {Number} x The x value of the point to stop avoiding. + * @param {Number} y The y value of the point to stop avoiding. + **/ + this.stopAvoidingAdditionalPoint = function(x, y) { + delete pointsToAvoid[x + "_" + y]; + }; + + /** + * Enables corner cutting in diagonal movement. + **/ + this.enableCornerCutting = function() { + allowCornerCutting = true; + }; + + /** + * Disables corner cutting in diagonal movement. + **/ + this.disableCornerCutting = function() { + allowCornerCutting = false; + }; + + /** + * Stop avoiding all additional points on the grid. + **/ + this.stopAvoidingAllAdditionalPoints = function() { + pointsToAvoid = {}; + }; + + /** + * Find a path. + * + * @param {Number} startX The X position of the starting point. + * @param {Number} startY The Y position of the starting point. + * @param {Number} endX The X position of the ending point. + * @param {Number} endY The Y position of the ending point. + * @param {Function} callback A function that is called when your path + * is found, or no path is found. + * + **/ + this.findPath = function(startX, startY, endX, endY, callback) { + // Wraps the callback for sync vs async logic + var callbackWrapper = function(result) { + if (syncEnabled) { + callback(result); + } else { + Script.setTimeout(function() { + callback(result); + }, 1); + } + } + + // No acceptable tiles were set + if (acceptableTiles === undefined) { + throw new Error("You can't set a path without first calling setAcceptableTiles() on EasyStar."); + } + // No grid was set + if (collisionGrid === undefined) { + throw new Error("You can't set a path without first calling setGrid() on EasyStar."); + } + + // Start or endpoint outside of scope. + if (startX < 0 || startY < 0 || endX < 0 || endX < 0 || + startX > collisionGrid[0].length - 1 || startY > collisionGrid.length - 1 || + endX > collisionGrid[0].length - 1 || endY > collisionGrid.length - 1) { + throw new Error("Your start or end point is outside the scope of your grid."); + } + + // Start and end are the same tile. + if (startX === endX && startY === endY) { + callbackWrapper([]); + return; + } + + // End point is not an acceptable tile. + var endTile = collisionGrid[endY][endX]; + var isAcceptable = false; + for (var i = 0; i < acceptableTiles.length; i++) { + if (endTile === acceptableTiles[i]) { + isAcceptable = true; + break; + } + } + + if (isAcceptable === false) { + callbackWrapper(null); + return; + } + + // Create the instance + var instance = new EasyStar.instance(); + instance.openList = new EasyStar.PriorityQueue("bestGuessDistance", EasyStar.PriorityQueue.MIN_HEAP); + instance.isDoneCalculating = false; + instance.nodeHash = {}; + instance.startX = startX; + instance.startY = startY; + instance.endX = endX; + instance.endY = endY; + instance.callback = callbackWrapper; + + instance.openList.insert(coordinateToNode(instance, instance.startX, + instance.startY, null, STRAIGHT_COST)); + + instances.push(instance); + }; + + /** + * This method steps through the A* Algorithm in an attempt to + * find your path(s). It will search 4-8 tiles (depending on diagonals) for every calculation. + * You can change the number of calculations done in a call by using + * easystar.setIteratonsPerCalculation(). + **/ + this.calculate = function() { + if (instances.length === 0 || collisionGrid === undefined || acceptableTiles === undefined) { + return; + } + for (iterationsSoFar = 0; iterationsSoFar < iterationsPerCalculation; iterationsSoFar++) { + if (instances.length === 0) { + return; + } + + if (syncEnabled) { + // If this is a sync instance, we want to make sure that it calculates synchronously. + iterationsSoFar = 0; + } + + // Couldn't find a path. + if (instances[0].openList.length === 0) { + var ic = instances[0]; + ic.callback(null); + instances.shift(); + continue; + } + + var searchNode = instances[0].openList.shiftHighestPriorityElement(); + + var tilesToSearch = []; + searchNode.list = EasyStar.Node.CLOSED_LIST; + + if (searchNode.y > 0) { + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: 0, + y: -1, + cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y - 1) + }); + } + if (searchNode.x < collisionGrid[0].length - 1) { + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: 1, + y: 0, + cost: STRAIGHT_COST * getTileCost(searchNode.x + 1, searchNode.y) + }); + } + if (searchNode.y < collisionGrid.length - 1) { + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: 0, + y: 1, + cost: STRAIGHT_COST * getTileCost(searchNode.x, searchNode.y + 1) + }); + } + if (searchNode.x > 0) { + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: -1, + y: 0, + cost: STRAIGHT_COST * getTileCost(searchNode.x - 1, searchNode.y) + }); + } + if (diagonalsEnabled) { + if (searchNode.x > 0 && searchNode.y > 0) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y))) { + + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: -1, + y: -1, + cost: DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y - 1) + }); + } + } + if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y < collisionGrid.length - 1) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y))) { + + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: 1, + y: 1, + cost: DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y + 1) + }); + } + } + if (searchNode.x < collisionGrid[0].length - 1 && searchNode.y > 0) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y - 1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x + 1, searchNode.y))) { + + + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: 1, + y: -1, + cost: DIAGONAL_COST * getTileCost(searchNode.x + 1, searchNode.y - 1) + }); + } + } + if (searchNode.x > 0 && searchNode.y < collisionGrid.length - 1) { + + if (allowCornerCutting || + (isTileWalkable(collisionGrid, acceptableTiles, searchNode.x, searchNode.y + 1) && + isTileWalkable(collisionGrid, acceptableTiles, searchNode.x - 1, searchNode.y))) { + + + tilesToSearch.push({ + instance: instances[0], + searchNode: searchNode, + x: -1, + y: 1, + cost: DIAGONAL_COST * getTileCost(searchNode.x - 1, searchNode.y + 1) + }); + } + } + } + + // First sort all of the potential nodes we could search by their cost + heuristic distance. + tilesToSearch.sort(function(a, b) { + var aCost = a.cost + getDistance(searchNode.x + a.x, searchNode.y + a.y, instances[0].endX, instances[0].endY) + var bCost = b.cost + getDistance(searchNode.x + b.x, searchNode.y + b.y, instances[0].endX, instances[0].endY) + if (aCost < bCost) { + return -1; + } else if (aCost === bCost) { + return 0; + } else { + return 1; + } + }); + + var isDoneCalculating = false; + + // Search all of the surrounding nodes + for (var i = 0; i < tilesToSearch.length; i++) { + checkAdjacentNode(tilesToSearch[i].instance, tilesToSearch[i].searchNode, + tilesToSearch[i].x, tilesToSearch[i].y, tilesToSearch[i].cost); + if (tilesToSearch[i].instance.isDoneCalculating === true) { + isDoneCalculating = true; + break; + } + } + + if (isDoneCalculating) { + instances.shift(); + continue; + } + + } + }; + + // Private methods follow + var checkAdjacentNode = function(instance, searchNode, x, y, cost) { + var adjacentCoordinateX = searchNode.x + x; + var adjacentCoordinateY = searchNode.y + y; + + if (pointsToAvoid[adjacentCoordinateX + "_" + adjacentCoordinateY] === undefined) { + // Handles the case where we have found the destination + if (instance.endX === adjacentCoordinateX && instance.endY === adjacentCoordinateY) { + instance.isDoneCalculating = true; + var path = []; + var pathLen = 0; + path[pathLen] = { + x: adjacentCoordinateX, + y: adjacentCoordinateY + }; + pathLen++; + path[pathLen] = { + x: searchNode.x, + y: searchNode.y + }; + pathLen++; + var parent = searchNode.parent; + while (parent != null) { + path[pathLen] = { + x: parent.x, + y: parent.y + }; + pathLen++; + parent = parent.parent; + } + path.reverse(); + var ic = instance; + var ip = path; + ic.callback(ip); + return + } + + if (isTileWalkable(collisionGrid, acceptableTiles, adjacentCoordinateX, adjacentCoordinateY)) { + var node = coordinateToNode(instance, adjacentCoordinateX, + adjacentCoordinateY, searchNode, cost); + + if (node.list === undefined) { + node.list = EasyStar.Node.OPEN_LIST; + instance.openList.insert(node); + } else if (node.list === EasyStar.Node.OPEN_LIST) { + if (searchNode.costSoFar + cost < node.costSoFar) { + node.costSoFar = searchNode.costSoFar + cost; + node.parent = searchNode; + } + } + } + } + }; + + // Helpers + var isTileWalkable = function(collisionGrid, acceptableTiles, x, y) { + for (var i = 0; i < acceptableTiles.length; i++) { + if (collisionGrid[y][x] === acceptableTiles[i]) { + return true; + } + } + + return false; + }; + + var getTileCost = function(x, y) { + return pointsToCost[x + '_' + y] || costMap[collisionGrid[y][x]] + }; + + var coordinateToNode = function(instance, x, y, parent, cost) { + if (instance.nodeHash[x + "_" + y] !== undefined) { + return instance.nodeHash[x + "_" + y]; + } + var simpleDistanceToTarget = getDistance(x, y, instance.endX, instance.endY); + if (parent !== null) { + var costSoFar = parent.costSoFar + cost; + } else { + costSoFar = simpleDistanceToTarget; + } + var node = new EasyStar.Node(parent, x, y, costSoFar, simpleDistanceToTarget); + instance.nodeHash[x + "_" + y] = node; + return node; + }; + + var getDistance = function(x1, y1, x2, y2) { + return Math.sqrt((x2 -= x1) * x2 + (y2 -= y1) * y2); + }; + } + return EasyStar +} \ No newline at end of file diff --git a/examples/libraries/easyStarExample.js b/examples/libraries/easyStarExample.js new file mode 100644 index 0000000000..3e28fb571e --- /dev/null +++ b/examples/libraries/easyStarExample.js @@ -0,0 +1,30 @@ +Script.include('easyStar.js'); +var easystar = loadEasyStar(); + +var grid = [ + [0, 0, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0] +]; + +easystar.setGrid(grid); + +easystar.setAcceptableTiles([0]); + +easystar.findPath(0, 0, 4, 0, function(path) { + if (path === null) { + print("Path was not found."); + Script.update.disconnect(tickEasyStar) + } else { + print("Path was found. The first Point is " + path[0].x + " " + path[0].y); + Script.update.disconnect(tickEasyStar) + } +}); + +var tickEasyStar = function() { + easystar.calculate(); +} + +Script.update.connect(tickEasyStar); \ No newline at end of file From 4fbff5d667ee2f5c9a4022802d37993eb4ada7a4 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 21:39:53 -0800 Subject: [PATCH 0782/1003] Update usertimingExample.js --- examples/libraries/usertimingExample.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/libraries/usertimingExample.js b/examples/libraries/usertimingExample.js index a43496a6b9..a1a0800d3d 100644 --- a/examples/libraries/usertimingExample.js +++ b/examples/libraries/usertimingExample.js @@ -1,16 +1,16 @@ Script.include('usertiming.js'); var timing = loadUserTiming(); //set a mark -timing.performance.mark('mark_fully_loaded') +timing.performance.mark('firstMark') + +//do something that takes time -- we're just going to set a timeout here as an example -var count = 0; Script.setTimeout(function() { - //do something that takes time //and set another mark - timing.performance.mark('mark_fully_loaded2') + timing.performance.mark('secondMark') //measure time between marks (first parameter is a name for the measurement) - timing.performance.measure('howlong', 'mark_fully_loaded', 'mark_fully_loaded2'); - //you can also get the marks, but the measure is usually what you're looking for - var items = timing.performance.getEntriesByType('measure'); - print('items is:::' + JSON.stringify(items)) -}, 1000) \ No newline at end of file + timing.performance.measure('howlong', 'firstMark', 'secondMark'); + //you can also get the marks by changing the type below + var measures = timing.performance.getEntriesByType('measure'); + print('measures:::' + JSON.stringify(measures)) +}, 1000) From 369eec2abdadae81760536359acd8e79935c8ebd Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 22:05:30 -0800 Subject: [PATCH 0783/1003] Update usertimingExample.js --- examples/libraries/usertimingExample.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/libraries/usertimingExample.js b/examples/libraries/usertimingExample.js index a1a0800d3d..cceb43435a 100644 --- a/examples/libraries/usertimingExample.js +++ b/examples/libraries/usertimingExample.js @@ -1,16 +1,18 @@ Script.include('usertiming.js'); var timing = loadUserTiming(); //set a mark -timing.performance.mark('firstMark') +timing.performance.mark('firstMark'); //do something that takes time -- we're just going to set a timeout here as an example Script.setTimeout(function() { //and set another mark - timing.performance.mark('secondMark') - //measure time between marks (first parameter is a name for the measurement) + timing.performance.mark('secondMark'); + + //measure time between marks (first parameter is a name for the measurement) timing.performance.measure('howlong', 'firstMark', 'secondMark'); - //you can also get the marks by changing the type below + + //you can also get the marks by changing the type var measures = timing.performance.getEntriesByType('measure'); print('measures:::' + JSON.stringify(measures)) }, 1000) From 34d98cac5eca2f8f0fcdb09a70e2cbf9ea1f45de Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 22:12:03 -0800 Subject: [PATCH 0784/1003] Update usertiming.js --- examples/libraries/usertiming.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/libraries/usertiming.js b/examples/libraries/usertiming.js index f477ed0bb7..9351972e37 100644 --- a/examples/libraries/usertiming.js +++ b/examples/libraries/usertiming.js @@ -3,7 +3,6 @@ // // A polyfill for UserTiming (http://www.w3.org/TR/user-timing/) // -// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015 // Copyright 2013 Nic Jansma // http://nicj.net // @@ -11,6 +10,7 @@ // // Licensed under the MIT license // +// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015 function userTiming() { "use strict"; @@ -544,4 +544,4 @@ function userTiming() { loadUserTiming = function() { return userTiming(); -} \ No newline at end of file +} From bf4320e5ccd507d15e7b5367399f2570f3a54b77 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 6 Nov 2015 22:55:33 -0800 Subject: [PATCH 0785/1003] re-add this example --- examples/controllers/rightClickExample.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 examples/controllers/rightClickExample.js diff --git a/examples/controllers/rightClickExample.js b/examples/controllers/rightClickExample.js new file mode 100644 index 0000000000..c3e6ea8f3d --- /dev/null +++ b/examples/controllers/rightClickExample.js @@ -0,0 +1,10 @@ +var MAPPING_NAME = "com.highfidelity.rightClickExample"; +var mapping = Controller.newMapping(MAPPING_NAME); +mapping.from(Controller.Hardware.Keyboard.RightMouseClicked).to(function (value) { + print("Keyboard.RightMouseClicked"); +}); +Controller.enableMapping(MAPPING_NAME); + +Script.scriptEnding.connect(function () { + Controller.disableMapping(MAPPING_NAME); +}); \ No newline at end of file From f3d26e163ddbb87e27e933b34b864962569d97f5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 7 Nov 2015 07:19:27 -0800 Subject: [PATCH 0786/1003] turn _drawItemStatus into a bitfield, split out bounding boxes and network/physics-sim-ownership. hook Display-Simulation-Ownership menu item to this system --- examples/utilities/tools/renderEngineDebug.js | 22 ++++++++++++++---- interface/src/Application.cpp | 7 +++--- .../render-utils/src/RenderDeferredTask.h | 7 +++++- libraries/render/src/render/DrawStatus.cpp | 23 +++++++++++-------- libraries/render/src/render/Engine.h | 6 ++++- .../src/SceneScriptingInterface.h | 6 ++--- 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index 3271741985..cca97b7184 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -62,10 +62,24 @@ var overlaysCounter = new CounterWidget(panel, "Overlays", ); -panel.newCheckbox("Display status", - function(value) { Scene.setEngineDisplayItemStatus(value); }, - function() { return Scene.doEngineDisplayItemStatus(); }, - function(value) { return (value); } +// see libraries/render/src/render/Engine.h +var showDisplayStatusFlag = 1; +var showNetworkStatusFlag = 2; + +panel.newCheckbox("Display status", + function(value) { Scene.setEngineDisplayItemStatus(value ? + Scene.doEngineDisplayItemStatus() | showDisplayStatusFlag : + Scene.doEngineDisplayItemStatus() & ~showDisplayStatusFlag); }, + function() { return (Scene.doEngineDisplayItemStatus() & showDisplayStatusFlag) > 0; }, + function(value) { return (value & showDisplayStatusFlag) > 0; } +); + +panel.newCheckbox("Network/Physics status", + function(value) { Scene.setEngineDisplayItemStatus(value ? + Scene.doEngineDisplayItemStatus() | showNetworkStatusFlag : + Scene.doEngineDisplayItemStatus() & ~showNetworkStatusFlag); }, + function() { return (Scene.doEngineDisplayItemStatus() & showNetworkStatusFlag) > 0; }, + function(value) { return (value & showNetworkStatusFlag) > 0; } ); var tickTackPeriod = 500; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 718d06c6e5..5a3d66be55 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3493,10 +3493,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) { renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_HULLS); } - if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) { - renderDebugFlags = - (RenderArgs::DebugFlags) (renderDebugFlags | (int)RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP); - } renderArgs->_debugFlags = renderDebugFlags; //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges); } @@ -3556,6 +3552,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems(); renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus(); + if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) { + renderContext._drawItemStatus |= render::showNetworkStatusFlag; + } renderContext._drawHitEffect = sceneInterface->doEngineDisplayHitEffect(); renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 04483fc037..6daa90b1ed 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -97,7 +97,12 @@ public: int _drawStatusJobIndex = -1; int _drawHitEffectJobIndex = -1; - void setDrawItemStatus(bool draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw); } } + void setDrawItemStatus(int draw) { + if (_drawStatusJobIndex >= 0) { + _jobs[_drawStatusJobIndex].setEnabled(draw > 0); + } + } + bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } } void setDrawHitEffect(bool draw) { if (_drawHitEffectJobIndex >= 0) { _jobs[_drawHitEffectJobIndex].setEnabled(draw); } } diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 57e21a1511..921098df06 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -144,20 +144,25 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex const unsigned int VEC3_ADRESS_OFFSET = 3; - for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); - batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + if ((renderContext->_drawItemStatus & showDisplayStatusFlag) > 0) { + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch.draw(gpu::LINES, 24, 0); + batch.draw(gpu::LINES, 24, 0); + } } batch.setPipeline(getDrawItemStatusPipeline()); - for (int i = 0; i < nbItems; i++) { - batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); - batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); - batch.draw(gpu::TRIANGLES, 24, 0); + if ((renderContext->_drawItemStatus & showNetworkStatusFlag) > 0) { + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); + + batch.draw(gpu::TRIANGLES, 24, 0); + } } }); } diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 5da7956b22..7c11246cff 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -25,6 +25,10 @@ public: }; typedef std::shared_ptr SceneContextPointer; +// see examples/utilities/tools/renderEngineDebug.js +const int showDisplayStatusFlag = 1; +const int showNetworkStatusFlag = 2; + class RenderContext { public: @@ -49,7 +53,7 @@ public: int _numDrawnOverlay3DItems = 0; int _maxDrawnOverlay3DItems = -1; - bool _drawItemStatus = false; + int _drawItemStatus = 0; bool _drawHitEffect = false; bool _occlusionStatus = false; diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 95919d6c0c..6be0ce44a8 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -107,8 +107,8 @@ public: Q_INVOKABLE void setEngineMaxDrawnOverlay3DItems(int count) { _maxDrawnOverlay3DItems = count; } Q_INVOKABLE int getEngineMaxDrawnOverlay3DItems() { return _maxDrawnOverlay3DItems; } - Q_INVOKABLE void setEngineDisplayItemStatus(bool display) { _drawItemStatus = display; } - Q_INVOKABLE bool doEngineDisplayItemStatus() { return _drawItemStatus; } + Q_INVOKABLE void setEngineDisplayItemStatus(int display) { _drawItemStatus = display; } + Q_INVOKABLE int doEngineDisplayItemStatus() { return _drawItemStatus; } Q_INVOKABLE void setEngineDisplayHitEffect(bool display) { _drawHitEffect = display; } Q_INVOKABLE bool doEngineDisplayHitEffect() { return _drawHitEffect; } @@ -143,7 +143,7 @@ protected: int _maxDrawnTransparentItems = -1; int _maxDrawnOverlay3DItems = -1; - bool _drawItemStatus = false; + int _drawItemStatus = 0; bool _drawHitEffect = false; From 82b26b75f4024099e569ef6703594adf3321759b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 9 Nov 2015 08:14:27 -0800 Subject: [PATCH 0787/1003] Code convention fixes --- tools/cache-extract/src/CacheExtractApp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/cache-extract/src/CacheExtractApp.h b/tools/cache-extract/src/CacheExtractApp.h index 3dde1f684d..3b34ff891d 100644 --- a/tools/cache-extract/src/CacheExtractApp.h +++ b/tools/cache-extract/src/CacheExtractApp.h @@ -22,9 +22,9 @@ // copy of QNetworkCacheMetaData class MyMetaData { public: - typedef QPair RawHeader; - typedef QList RawHeaderList; - typedef QHash AttributesMap; + using RawHeader = QPair; + using RawHeaderList = QList; + using AttributesMap = QHash; QUrl url; QDateTime expirationDate; @@ -34,7 +34,7 @@ public: RawHeaderList rawHeaders; }; -QDataStream &operator>>(QDataStream &, MyMetaData &); +QDataStream &operator>>(QDataStream& in, MyMetaData& metaData); class CacheExtractApp : public QCoreApplication { Q_OBJECT From 2ecc4f8a5c88dd32d3d3ba05f8a01c3a119d23f1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 9 Nov 2015 08:58:24 -0800 Subject: [PATCH 0788/1003] Prevent sixense manager from triggering continuous reset --- .../src/input-plugins/SixenseManager.cpp | 37 +++++++++---------- .../src/input-plugins/SixenseManager.h | 18 ++++++++- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 3085b377f0..1413ef0ce1 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -108,19 +108,13 @@ void SixenseManager::setSixenseFilter(bool filter) { void SixenseManager::pluginUpdate(float deltaTime, bool jointsCaptured) { _inputDevice->update(deltaTime, jointsCaptured); - if (_inputDevice->_calibrationState == CALIBRATION_STATE_COMPLETE) { + if (_inputDevice->_requestReset) { _container->requestReset(); - _inputDevice->_calibrationState = CALIBRATION_STATE_IDLE; + _inputDevice->_requestReset = false; } } void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { - // 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(); @@ -221,21 +215,23 @@ static const float MINIMUM_ARM_REACH = 0.3f; // meters static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired -void SixenseManager::InputDevice::updateCalibration(void* controllersX) { - auto controllers = reinterpret_cast(controllersX); +static bool calibrationRequested(sixenseControllerData* controllers) { + return (controllers[0].buttons == BUTTON_FWD && controllers[1].buttons == BUTTON_FWD); +} + +void SixenseManager::InputDevice::updateCalibration(sixenseControllerData* controllers) { const sixenseControllerData* dataLeft = controllers; const sixenseControllerData* dataRight = controllers + 1; - // calibration only happpens while both hands are holding BUTTON_FORWARD - if (dataLeft->buttons != BUTTON_FWD || dataRight->buttons != BUTTON_FWD) { - if (_calibrationState == CALIBRATION_STATE_IDLE) { - return; - } + // Calibration buttons aren't set, so check the state, and request a reset if necessary. + if (!calibrationRequested(controllers)) { switch (_calibrationState) { + case CALIBRATION_STATE_IDLE: + return; case CALIBRATION_STATE_COMPLETE: { // compute calibration results - _avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands + _avatarPosition = -0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y)); xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis)); @@ -243,16 +239,19 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) { const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f; _avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR; qCDebug(inputplugins, "succeess: sixense calibration"); + _requestReset = true; } break; default: - _calibrationState = CALIBRATION_STATE_IDLE; qCDebug(inputplugins, "failed: sixense calibration"); break; } + + _calibrationState = CALIBRATION_STATE_IDLE; return; } + // Calibration buttons are set, continue calibration work // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. const float* pos = dataLeft->pos; glm::vec3 positionLeft(pos[0], pos[1], pos[2]); @@ -261,6 +260,7 @@ void SixenseManager::InputDevice::updateCalibration(void* controllersX) { glm::vec3 positionRight(pos[0], pos[1], pos[2]); positionRight *= METERS_PER_MILLIMETER; + // Gather initial calibration data if (_calibrationState == CALIBRATION_STATE_IDLE) { float reach = glm::distance(positionLeft, positionRight); if (reach > 2.0f * MINIMUM_ARM_REACH) { @@ -308,9 +308,6 @@ void SixenseManager::InputDevice::focusOutEvent() { _buttonPressedMap.clear(); }; -void SixenseManager::InputDevice::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { -} - void SixenseManager::InputDevice::handleButtonEvent(unsigned int buttons, bool left) { using namespace controller; if (buttons & BUTTON_0) { diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index bdfbd539cb..8452e1f096 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -19,6 +19,20 @@ #include "InputPlugin.h" +class QLibrary; + +const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 +const unsigned int BUTTON_1 = 1U << 5; +const unsigned int BUTTON_2 = 1U << 6; +const unsigned int BUTTON_3 = 1U << 3; +const unsigned int BUTTON_4 = 1U << 4; +const unsigned int BUTTON_FWD = 1U << 7; +const unsigned int BUTTON_TRIGGER = 1U << 8; + +const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; + +struct _sixenseControllerData; + // Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public InputPlugin { Q_OBJECT @@ -65,9 +79,8 @@ private: virtual void focusOutEvent() override; void handleButtonEvent(unsigned int buttons, bool left); - void handleAxisEvent(float x, float y, float trigger, bool left); void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - void updateCalibration(void* controllers); + void updateCalibration(_sixenseControllerData* controllers); friend class SixenseManager; @@ -79,6 +92,7 @@ private: glm::quat _avatarRotation; // in hydra-frame float _lastDistance; + bool _requestReset { false }; // these are measured values used to compute the calibration results quint64 _lockExpiry; glm::vec3 _averageLeft; From 07be03dc7e244ec4dcc27f18fcc1c595deff5a93 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 9 Nov 2015 09:27:31 -0800 Subject: [PATCH 0789/1003] Bad merge fix, PR feedback --- .../src/input-plugins/SixenseManager.cpp | 16 ++++++++-------- .../src/input-plugins/SixenseManager.h | 15 ++------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp index 1413ef0ce1..18fdc9ddad 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.cpp +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.cpp @@ -147,14 +147,14 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) { int maxControllers = sixenseGetMaxControllers(); // we only support two controllers - sixenseControllerData controllers[2]; + SixenseControllerData controllers[2]; int numActiveControllers = 0; for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) { if (!sixenseIsControllerEnabled(i)) { continue; } - sixenseControllerData* data = controllers + numActiveControllers; + SixenseControllerData* data = controllers + numActiveControllers; ++numActiveControllers; sixenseGetNewestData(i, data); @@ -215,21 +215,21 @@ static const float MINIMUM_ARM_REACH = 0.3f; // meters static const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters static const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired -static bool calibrationRequested(sixenseControllerData* controllers) { +static bool calibrationRequested(SixenseControllerData* controllers) { return (controllers[0].buttons == BUTTON_FWD && controllers[1].buttons == BUTTON_FWD); } -void SixenseManager::InputDevice::updateCalibration(sixenseControllerData* controllers) { - const sixenseControllerData* dataLeft = controllers; - const sixenseControllerData* dataRight = controllers + 1; +void SixenseManager::InputDevice::updateCalibration(SixenseControllerData* controllers) { + const SixenseControllerData* dataLeft = controllers; + const SixenseControllerData* dataRight = controllers + 1; // Calibration buttons aren't set, so check the state, and request a reset if necessary. if (!calibrationRequested(controllers)) { switch (_calibrationState) { case CALIBRATION_STATE_IDLE: return; - case CALIBRATION_STATE_COMPLETE: - { + + case CALIBRATION_STATE_COMPLETE: { // compute calibration results _avatarPosition = -0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft); diff --git a/libraries/input-plugins/src/input-plugins/SixenseManager.h b/libraries/input-plugins/src/input-plugins/SixenseManager.h index 8452e1f096..348a7a4590 100644 --- a/libraries/input-plugins/src/input-plugins/SixenseManager.h +++ b/libraries/input-plugins/src/input-plugins/SixenseManager.h @@ -19,19 +19,8 @@ #include "InputPlugin.h" -class QLibrary; - -const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2 -const unsigned int BUTTON_1 = 1U << 5; -const unsigned int BUTTON_2 = 1U << 6; -const unsigned int BUTTON_3 = 1U << 3; -const unsigned int BUTTON_4 = 1U << 4; -const unsigned int BUTTON_FWD = 1U << 7; -const unsigned int BUTTON_TRIGGER = 1U << 8; - -const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; - struct _sixenseControllerData; +using SixenseControllerData = _sixenseControllerData; // Handles interaction with the Sixense SDK (e.g., Razer Hydra). class SixenseManager : public InputPlugin { @@ -80,7 +69,7 @@ private: void handleButtonEvent(unsigned int buttons, bool left); void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); - void updateCalibration(_sixenseControllerData* controllers); + void updateCalibration(SixenseControllerData* controllers); friend class SixenseManager; From ef7caa8da9ddd02763b06978ce0c8359f7ec7c6e Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 9 Nov 2015 10:48:12 -0800 Subject: [PATCH 0790/1003] Add new collision hull and sound to ping pong gun --- examples/toybox/ping_pong_gun/createPingPongGun.js | 10 +++++----- unpublishedScripts/hiddenEntityReset.js | 8 +++++--- unpublishedScripts/masterReset.js | 8 +++++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index cfeaba7f4e..9639f75320 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -14,8 +14,8 @@ Script.include("../../utilities.js"); var scriptURL = Script.resolvePath('pingPongGun.js'); var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx' -var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - +var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj'; +var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav'; var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, @@ -25,9 +25,8 @@ var center = Vec3.sum(Vec3.sum(MyAvatar.position, { var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType:'box', - // shapeType: 'compound', - // compoundShapeURL: COLLISION_HULL_URL, + shapeType: 'compound', + compoundShapeURL: COLLISION_HULL_URL, script: scriptURL, position: center, dimensions: { @@ -36,6 +35,7 @@ var pingPongGun = Entities.addEntity({ z: 0.47 }, collisionsWillMove: true, + collisionSoundURL: COLLISION_SOUND_URL }); function cleanUp() { diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 0773cc5640..3dd66580c5 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -887,8 +887,8 @@ function createPingPongBallGun() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj'; + var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav'; var position = { x: 548.6, y: 495.4, @@ -900,7 +900,8 @@ var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType: 'box', + shapeType: 'compound', + compoundShapeURL: COLLISION_SOUND_URL, script: pingPongScriptURL, position: position, rotation: rotation, @@ -915,6 +916,7 @@ z: 0.47 }, collisionsWillMove: true, + collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ resetMe: { resetMe: true diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index aa7a1912de..7236d04a9d 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -869,8 +869,8 @@ MasterReset = function() { function createPingPongBallGun() { var MODEL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun.fbx'; - var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_collision_hull.obj'; - + var COLLISION_HULL_URL = 'http://hifi-public.s3.amazonaws.com/models/ping_pong_gun/ping_pong_gun_convex.obj'; + var COLLISION_SOUND_URL = 'http://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/plastic_impact.L.wav'; var position = { x: 548.6, y: 495.4, @@ -882,7 +882,8 @@ MasterReset = function() { var pingPongGun = Entities.addEntity({ type: "Model", modelURL: MODEL_URL, - shapeType: 'box', + shapeType: 'compound', + compoundShapeURL:COLLISION_SOUND_URL, script: pingPongScriptURL, position: position, rotation: rotation, @@ -897,6 +898,7 @@ MasterReset = function() { z: 0.47 }, collisionsWillMove: true, + collisionSoundURL: COLLISION_SOUND_URL, userData: JSON.stringify({ resetMe: { resetMe: true From b8357112660a0955077056d138acbe1e9d5e9d81 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 9 Nov 2015 12:04:17 -0800 Subject: [PATCH 0791/1003] Update script engine path resolution behavior The path resolution will now be relative to the script currently being evaluated *on its initial evaluation.* The previous behavior was that all paths would be resolved relative to the root script for client scripts, and inconsistent for entity scripts depending on the order that scripts were loaded. The entity script situation was particularly bad because including more than 1 level deep produced inconsistent results. --- libraries/script-engine/src/ScriptEngine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 381eef63db..e2a3143026 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -901,14 +901,19 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac BatchLoader* loader = new BatchLoader(urls); auto evaluateScripts = [=](const QMap& data) { + auto parentURL = _parentURL; for (QUrl url : urls) { QString contents = data[url]; if (contents.isNull()) { qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__; } else { + // Set the parent url so that path resolution will be relative + // to this script's url during its initial evaluation + _parentURL = url.toString(); QScriptValue result = evaluate(contents, url.toString()); } } + _parentURL = parentURL; if (callback.isFunction()) { QScriptValue(callback).call(); From 36dfe81fde1694242951c2fa8098dc715b817387 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 9 Nov 2015 12:19:23 -0800 Subject: [PATCH 0792/1003] fix link --- unpublishedScripts/hiddenEntityReset.js | 2 +- unpublishedScripts/masterReset.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 3dd66580c5..a8b4383b04 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -901,7 +901,7 @@ type: "Model", modelURL: MODEL_URL, shapeType: 'compound', - compoundShapeURL: COLLISION_SOUND_URL, + compoundShapeURL: COLLISION_HULL_URL, script: pingPongScriptURL, position: position, rotation: rotation, diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 7236d04a9d..1b4c83a1d5 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -883,7 +883,7 @@ MasterReset = function() { type: "Model", modelURL: MODEL_URL, shapeType: 'compound', - compoundShapeURL:COLLISION_SOUND_URL, + compoundShapeURL:COLLISION_HULL_URL, script: pingPongScriptURL, position: position, rotation: rotation, From 2716a9f2b4ec049d1c2e8b934e5707f23e1799a4 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 9 Nov 2015 13:11:03 -0800 Subject: [PATCH 0793/1003] fix IK for feet targets --- libraries/animation/src/AnimInverseKinematics.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 6355426dae..691197a651 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -160,7 +160,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(pivotIndex); - if (pivotsParentIndex == -1 || pivotIndex == _hipsIndex) { + if (pivotsParentIndex == -1) { // TODO?: handle case where tip's parent is root? continue; } @@ -173,7 +173,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(i); - if (parentIndex != -1 && parentIndex != _hipsIndex) { + if (parentIndex != -1) { absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i]; } } @@ -295,7 +295,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vectorgetParentIndex(tipIndex); - if (parentIndex != -1 && parentIndex != _hipsIndex) { + if (parentIndex != -1) { const glm::quat& targetRotation = target.getRotation(); // compute tip's new parent-relative rotation // Q = Qp * q --> q' = Qp^ * Q From 8a434c8ab14bb2de5b84189d1a3d443afe207f04 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 9 Nov 2015 14:10:26 -0800 Subject: [PATCH 0794/1003] don't attempt to near-grab things that aren't physical --- 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 5035f2e5a4..d308b3dc49 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -489,7 +489,7 @@ function MyController(hand) { if (grabbableData.wantsTrigger) { this.setState(STATE_NEAR_TRIGGER); return; - } else if (!props.locked) { + } else if (!props.locked && props.collisionsWillMove) { this.setState(STATE_NEAR_GRABBING); return; } From c07b97920e1152ea0e34dd0930b0e865b1c66d75 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 9 Nov 2015 14:11:45 -0800 Subject: [PATCH 0795/1003] fix for avatars created with blender --- libraries/animation/src/AnimInverseKinematics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 691197a651..c7d1a9d630 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -173,7 +173,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector Date: Mon, 9 Nov 2015 14:15:09 -0800 Subject: [PATCH 0796/1003] swap the order of checks --- libraries/animation/src/AnimInverseKinematics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index c7d1a9d630..d5728b79b4 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -173,7 +173,7 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector Date: Mon, 9 Nov 2015 14:26:23 -0800 Subject: [PATCH 0797/1003] Fix warnings on OS X --- interface/src/avatar/MyAvatar.cpp | 3 --- tests/recording/src/main.cpp | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 57af9e732d..8595fa850e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1608,7 +1608,6 @@ void MyAvatar::updateOrientation(float 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) { totalBodyYaw += _driveKeys[STEP_YAW]; } @@ -1676,8 +1675,6 @@ 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 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? diff --git a/tests/recording/src/main.cpp b/tests/recording/src/main.cpp index 836d8b5ac1..96c9a7f1a7 100644 --- a/tests/recording/src/main.cpp +++ b/tests/recording/src/main.cpp @@ -12,8 +12,6 @@ #include "Constants.h" -#define QVERIFY Q_ASSERT - using namespace recording; FrameType TEST_FRAME_TYPE { Frame::TYPE_INVALID }; @@ -30,7 +28,8 @@ void testFrameTypeRegistration() { auto backMap = recording::Frame::getFrameTypeNames(); QVERIFY(backMap.count(TEST_FRAME_TYPE) == 1); QVERIFY(backMap[TEST_FRAME_TYPE] == TEST_NAME); - QVERIFY(backMap[recording::Frame::TYPE_HEADER] == HEADER_NAME); + auto typeHeader = recording::Frame::TYPE_HEADER; + QVERIFY(backMap[typeHeader] == HEADER_NAME); } void testFilePersist() { From e47b049200c6679fb266a99777f229d77796611a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 9 Nov 2015 16:41:40 -0800 Subject: [PATCH 0798/1003] fix render item leak on Model::reset() --- libraries/render-utils/src/Model.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6aae7ad1cb..2cb380a8e2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -135,9 +135,6 @@ void Model::reset() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); _rig->reset(geometry.joints); } - _meshGroupsKnown = false; - _readyWhenAdded = false; // in case any of our users are using scenes - invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid } bool Model::updateGeometry() { From 021a6e689d92c26a4d2a84c6f0426bc592d8b4e7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 9 Nov 2015 17:09:17 -0800 Subject: [PATCH 0799/1003] added some asserts to test future mistakes that would cause render item leaks --- libraries/render-utils/src/Model.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2cb380a8e2..5b9bfdca3d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1153,6 +1153,9 @@ void Model::segregateMeshGroups() { return; } + Q_ASSERT(_renderItems.isEmpty()); // We should not have any existing renderItems if we enter this section of code + Q_ASSERT(_renderItemsSet.isEmpty()); // We should not have any existing renderItemsSet if we enter this section of code + _renderItemsSet.clear(); // Run through all of the meshes, and place them into their segregated, but unsorted buckets From 17814446ca3306f75b5821a87076aad8f5375dfe Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 9 Nov 2015 17:11:09 -0800 Subject: [PATCH 0800/1003] Add example with entities, add tween.js --- examples/libraries/easyStar.js | 1 - examples/libraries/easyStarExample.js | 214 ++++++- examples/libraries/tween.js | 878 ++++++++++++++++++++++++++ 3 files changed, 1082 insertions(+), 11 deletions(-) create mode 100644 examples/libraries/tween.js diff --git a/examples/libraries/easyStar.js b/examples/libraries/easyStar.js index b76ff1d7ec..15166c87c5 100644 --- a/examples/libraries/easyStar.js +++ b/examples/libraries/easyStar.js @@ -302,7 +302,6 @@ var eStar = function() { * This should be a 2D Array of Numbers. **/ this.setGrid = function(grid) { - print('EASY GRID') collisionGrid = grid; //Setup cost map diff --git a/examples/libraries/easyStarExample.js b/examples/libraries/easyStarExample.js index 3e28fb571e..eb758a2fe3 100644 --- a/examples/libraries/easyStarExample.js +++ b/examples/libraries/easyStarExample.js @@ -1,25 +1,45 @@ +// +// easyStarExample.js +// +// Created by James B. Pollack @imgntn on 11/9/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This is a script that sets up a grid of obstacles and passable tiles, finds a path, and then moves an entity along the path. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +// To-Do: +// Abstract start position and make tiles, spheres, etc. relative +// Handle dynamically changing grids + Script.include('easyStar.js'); var easystar = loadEasyStar(); +Script.include('tween.js'); +var TWEEN = loadTween(); +var ANIMATION_DURATION = 350; var grid = [ - [0, 0, 1, 0, 0], - [0, 0, 1, 0, 0], - [0, 0, 1, 0, 0], - [0, 0, 1, 0, 0], - [0, 0, 0, 0, 0] + [0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 0, 1, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 1], + [0, 0, 1, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0] ]; easystar.setGrid(grid); easystar.setAcceptableTiles([0]); -easystar.findPath(0, 0, 4, 0, function(path) { +easystar.findPath(0, 0, 8, 0, function(path) { if (path === null) { print("Path was not found."); - Script.update.disconnect(tickEasyStar) + Script.update.disconnect(tickEasyStar); } else { - print("Path was found. The first Point is " + path[0].x + " " + path[0].y); - Script.update.disconnect(tickEasyStar) + print('path' + JSON.stringify(path)); + convertPath(path); + Script.update.disconnect(tickEasyStar); } }); @@ -27,4 +47,178 @@ var tickEasyStar = function() { easystar.calculate(); } -Script.update.connect(tickEasyStar); \ No newline at end of file +Script.update.connect(tickEasyStar); + +//a sphere that will get moved +var playerSphere = Entities.addEntity({ + type: 'Sphere', + shape: 'Sphere', + color: { + red: 255, + green: 0, + blue: 0 + }, + dimensions: { + x: 0.5, + y: 0.5, + z: 0.5 + }, + position: { + x: 0, + y: 0, + z: 0 + } +}) + + +//for keeping track of entities +var obstacles = []; +var passables = []; + +function createPassableAtTilePosition(posX, posY) { + var properties = { + type: 'Box', + shapeType: 'Box', + dimensions: { + x: 1, + y: 1, + z: 1 + }, + position: { + x: posY, + y: -1, + z: posX + }, + color: { + red: 0, + green: 0, + blue: 255 + } + }; + var passable = Entities.addEntity(properties); + passables.push(passable); +} + +function createObstacleAtTilePosition(posX, posY) { + var properties = { + type: 'Box', + shapeType: 'Box', + dimensions: { + x: 1, + y: 1, + z: 1 + }, + position: { + x: posY, + y: 0, + z: posX + }, + color: { + red: 0, + green: 255, + blue: 0 + } + }; + var obstacle = Entities.addEntity(properties); + obstacles.push(obstacle); +} + +function createObstacles(grid) { + grid.forEach(function(row, rowIndex) { + row.forEach(function(v, index) { + if (v === 1) { + createObstacleAtTilePosition(rowIndex, index); + } + if (v === 0) { + createPassableAtTilePosition(rowIndex, index); + } + }) + + }) +} + + + +createObstacles(grid); + + +var currentSpherePosition = { + x: 0, + y: 0, + z: 0 +}; + + +function convertPathPointToCoordinates(x, y) { + return { + x: y, + y: 0, + z: x + }; +} + +var convertedPath = []; + +//convert our path to Vec3s +function convertPath(path) { + path.forEach(function(point) { + var convertedPoint = convertPathPointToCoordinates(point.x, point.y); + convertedPath.push(convertedPoint); + }); + createTweenPath(convertedPath); +} + +function updatePosition() { + Entities.editEntity(playerSphere, { + position: { + x: currentSpherePosition.z, + y: currentSpherePosition.y, + z: currentSpherePosition.x + } + }); +} + +function createTweenPath(convertedPath) { + var i; + var stepTweens = []; + + //create the tweens + for (i = 0; i < convertedPath.length - 1; i++) { + var stepTween = new TWEEN.Tween(currentSpherePosition).to(convertedPath[i + 1], ANIMATION_DURATION).onUpdate(updatePosition).onComplete(tweenStep); + stepTweens.push(stepTween); + } + + var j; + //chain one tween to the next + for (j = 0; j < stepTweens.length - 1; j++) { + stepTweens[j].chain(stepTweens[j + 1]); + } + + //start the tween + stepTweens[0].start(); +} + +function tweenStep() { + // print('step between tweens') +} + +function updateTweens() { + //hook tween updates into our update loop + TWEEN.update(); +} + +Script.update.connect(updateTweens); + +function cleanup() { + while (obstacles.length > 0) { + Entities.deleteEntity(obstacles.pop()); + } + while (passables.length > 0) { + Entities.deleteEntity(passables.pop()); + } + Entities.deleteEntity(playerSphere); + Script.update.disconnect(updateTweens); +} + + +Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/libraries/tween.js b/examples/libraries/tween.js new file mode 100644 index 0000000000..f50e9533ae --- /dev/null +++ b/examples/libraries/tween.js @@ -0,0 +1,878 @@ +/** + * Tween.js - Licensed under the MIT license + * https://github.com/tweenjs/tween.js + * ---------------------------------------------- + * + * See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors. + * Thank you all, you're awesome! + */ + +// Include a performance.now polyfill +(function () { + window= {} + if ('performance' in window === false) { + window.performance = {}; + } + + // IE 8 + Date.now = (Date.now || function () { + return new Date().getTime(); + }); + + if ('now' in window.performance === false) { + var offset = window.performance.timing && window.performance.timing.navigationStart ? window.performance.timing.navigationStart + : Date.now(); + + window.performance.now = function () { + return Date.now() - offset; + }; + } + +})(); + +var TWEEN = TWEEN || (function () { + + var _tweens = []; + + return { + + getAll: function () { + + return _tweens; + + }, + + removeAll: function () { + + _tweens = []; + + }, + + add: function (tween) { + + _tweens.push(tween); + + }, + + remove: function (tween) { + + var i = _tweens.indexOf(tween); + + if (i !== -1) { + _tweens.splice(i, 1); + } + + }, + + update: function (time) { + + if (_tweens.length === 0) { + return false; + } + + var i = 0; + + time = time !== undefined ? time : window.performance.now(); + + while (i < _tweens.length) { + + if (_tweens[i].update(time)) { + i++; + } else { + _tweens.splice(i, 1); + } + + } + + return true; + + } + }; + +})(); + +TWEEN.Tween = function (object) { + + var _object = object; + var _valuesStart = {}; + var _valuesEnd = {}; + var _valuesStartRepeat = {}; + var _duration = 1000; + var _repeat = 0; + var _yoyo = false; + var _isPlaying = false; + var _reversed = false; + var _delayTime = 0; + var _startTime = null; + var _easingFunction = TWEEN.Easing.Linear.None; + var _interpolationFunction = TWEEN.Interpolation.Linear; + var _chainedTweens = []; + var _onStartCallback = null; + var _onStartCallbackFired = false; + var _onUpdateCallback = null; + var _onCompleteCallback = null; + var _onStopCallback = null; + + // Set all starting values present on the target object + for (var field in object) { + _valuesStart[field] = parseFloat(object[field], 10); + } + + this.to = function (properties, duration) { + + if (duration !== undefined) { + _duration = duration; + } + + _valuesEnd = properties; + + return this; + + }; + + this.start = function (time) { + + TWEEN.add(this); + + _isPlaying = true; + + _onStartCallbackFired = false; + + _startTime = time !== undefined ? time : window.performance.now(); + _startTime += _delayTime; + + for (var property in _valuesEnd) { + + // Check if an Array was provided as property value + if (_valuesEnd[property] instanceof Array) { + + if (_valuesEnd[property].length === 0) { + continue; + } + + // Create a local copy of the Array with the start value at the front + _valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]); + + } + + _valuesStart[property] = _object[property]; + + if ((_valuesStart[property] instanceof Array) === false) { + _valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings + } + + _valuesStartRepeat[property] = _valuesStart[property] || 0; + + } + + return this; + + }; + + this.stop = function () { + + if (!_isPlaying) { + return this; + } + + TWEEN.remove(this); + _isPlaying = false; + + if (_onStopCallback !== null) { + _onStopCallback.call(_object); + } + + this.stopChainedTweens(); + return this; + + }; + + this.stopChainedTweens = function () { + + for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) { + _chainedTweens[i].stop(); + } + + }; + + this.delay = function (amount) { + + _delayTime = amount; + return this; + + }; + + this.repeat = function (times) { + + _repeat = times; + return this; + + }; + + this.yoyo = function (yoyo) { + + _yoyo = yoyo; + return this; + + }; + + + this.easing = function (easing) { + + _easingFunction = easing; + return this; + + }; + + this.interpolation = function (interpolation) { + + _interpolationFunction = interpolation; + return this; + + }; + + this.chain = function () { + + _chainedTweens = arguments; + return this; + + }; + + this.onStart = function (callback) { + + _onStartCallback = callback; + return this; + + }; + + this.onUpdate = function (callback) { + + _onUpdateCallback = callback; + return this; + + }; + + this.onComplete = function (callback) { + + _onCompleteCallback = callback; + return this; + + }; + + this.onStop = function (callback) { + + _onStopCallback = callback; + return this; + + }; + + this.update = function (time) { + + var property; + var elapsed; + var value; + + if (time < _startTime) { + return true; + } + + if (_onStartCallbackFired === false) { + + if (_onStartCallback !== null) { + _onStartCallback.call(_object); + } + + _onStartCallbackFired = true; + + } + + elapsed = (time - _startTime) / _duration; + elapsed = elapsed > 1 ? 1 : elapsed; + + value = _easingFunction(elapsed); + + for (property in _valuesEnd) { + + var start = _valuesStart[property] || 0; + var end = _valuesEnd[property]; + + if (end instanceof Array) { + + _object[property] = _interpolationFunction(end, value); + + } else { + + // Parses relative end values with start as base (e.g.: +10, -3) + if (typeof (end) === 'string') { + end = start + parseFloat(end, 10); + } + + // Protect against non numeric properties. + if (typeof (end) === 'number') { + _object[property] = start + (end - start) * value; + } + + } + + } + + if (_onUpdateCallback !== null) { + _onUpdateCallback.call(_object, value); + } + + if (elapsed === 1) { + + if (_repeat > 0) { + + if (isFinite(_repeat)) { + _repeat--; + } + + // Reassign starting values, restart by making startTime = now + for (property in _valuesStartRepeat) { + + if (typeof (_valuesEnd[property]) === 'string') { + _valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property], 10); + } + + if (_yoyo) { + var tmp = _valuesStartRepeat[property]; + + _valuesStartRepeat[property] = _valuesEnd[property]; + _valuesEnd[property] = tmp; + } + + _valuesStart[property] = _valuesStartRepeat[property]; + + } + + if (_yoyo) { + _reversed = !_reversed; + } + + _startTime = time + _delayTime; + + return true; + + } else { + + if (_onCompleteCallback !== null) { + _onCompleteCallback.call(_object); + } + + for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) { + // Make the chained tweens start exactly at the time they should, + // even if the `update()` method was called way past the duration of the tween + _chainedTweens[i].start(_startTime + _duration); + } + + return false; + + } + + } + + return true; + + }; + +}; + + +TWEEN.Easing = { + + Linear: { + + None: function (k) { + + return k; + + } + + }, + + Quadratic: { + + In: function (k) { + + return k * k; + + }, + + Out: function (k) { + + return k * (2 - k); + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k; + } + + return - 0.5 * (--k * (k - 2) - 1); + + } + + }, + + Cubic: { + + In: function (k) { + + return k * k * k; + + }, + + Out: function (k) { + + return --k * k * k + 1; + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k * k; + } + + return 0.5 * ((k -= 2) * k * k + 2); + + } + + }, + + Quartic: { + + In: function (k) { + + return k * k * k * k; + + }, + + Out: function (k) { + + return 1 - (--k * k * k * k); + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k; + } + + return - 0.5 * ((k -= 2) * k * k * k - 2); + + } + + }, + + Quintic: { + + In: function (k) { + + return k * k * k * k * k; + + }, + + Out: function (k) { + + return --k * k * k * k * k + 1; + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k * k; + } + + return 0.5 * ((k -= 2) * k * k * k * k + 2); + + } + + }, + + Sinusoidal: { + + In: function (k) { + + return 1 - Math.cos(k * Math.PI / 2); + + }, + + Out: function (k) { + + return Math.sin(k * Math.PI / 2); + + }, + + InOut: function (k) { + + return 0.5 * (1 - Math.cos(Math.PI * k)); + + } + + }, + + Exponential: { + + In: function (k) { + + return k === 0 ? 0 : Math.pow(1024, k - 1); + + }, + + Out: function (k) { + + return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); + + }, + + InOut: function (k) { + + if (k === 0) { + return 0; + } + + if (k === 1) { + return 1; + } + + if ((k *= 2) < 1) { + return 0.5 * Math.pow(1024, k - 1); + } + + return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2); + + } + + }, + + Circular: { + + In: function (k) { + + return 1 - Math.sqrt(1 - k * k); + + }, + + Out: function (k) { + + return Math.sqrt(1 - (--k * k)); + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return - 0.5 * (Math.sqrt(1 - k * k) - 1); + } + + return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); + + } + + }, + + Elastic: { + + In: function (k) { + + var s; + var a = 0.1; + var p = 0.4; + + if (k === 0) { + return 0; + } + + if (k === 1) { + return 1; + } + + if (!a || a < 1) { + a = 1; + s = p / 4; + } else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + + return - (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p)); + + }, + + Out: function (k) { + + var s; + var a = 0.1; + var p = 0.4; + + if (k === 0) { + return 0; + } + + if (k === 1) { + return 1; + } + + if (!a || a < 1) { + a = 1; + s = p / 4; + } else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + + return (a * Math.pow(2, - 10 * k) * Math.sin((k - s) * (2 * Math.PI) / p) + 1); + + }, + + InOut: function (k) { + + var s; + var a = 0.1; + var p = 0.4; + + if (k === 0) { + return 0; + } + + if (k === 1) { + return 1; + } + + if (!a || a < 1) { + a = 1; + s = p / 4; + } else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + + if ((k *= 2) < 1) { + return - 0.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p)); + } + + return a * Math.pow(2, -10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; + + } + + }, + + Back: { + + In: function (k) { + + var s = 1.70158; + + return k * k * ((s + 1) * k - s); + + }, + + Out: function (k) { + + var s = 1.70158; + + return --k * k * ((s + 1) * k + s) + 1; + + }, + + InOut: function (k) { + + var s = 1.70158 * 1.525; + + if ((k *= 2) < 1) { + return 0.5 * (k * k * ((s + 1) * k - s)); + } + + return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); + + } + + }, + + Bounce: { + + In: function (k) { + + return 1 - TWEEN.Easing.Bounce.Out(1 - k); + + }, + + Out: function (k) { + + if (k < (1 / 2.75)) { + return 7.5625 * k * k; + } else if (k < (2 / 2.75)) { + return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; + } else if (k < (2.5 / 2.75)) { + return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; + } else { + return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; + } + + }, + + InOut: function (k) { + + if (k < 0.5) { + return TWEEN.Easing.Bounce.In(k * 2) * 0.5; + } + + return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5; + + } + + } + +}; + +TWEEN.Interpolation = { + + Linear: function (v, k) { + + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + var fn = TWEEN.Interpolation.Utils.Linear; + + if (k < 0) { + return fn(v[0], v[1], f); + } + + if (k > 1) { + return fn(v[m], v[m - 1], m - f); + } + + return fn(v[i], v[i + 1 > m ? m : i + 1], f - i); + + }, + + Bezier: function (v, k) { + + var b = 0; + var n = v.length - 1; + var pw = Math.pow; + var bn = TWEEN.Interpolation.Utils.Bernstein; + + for (var i = 0; i <= n; i++) { + b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i); + } + + return b; + + }, + + CatmullRom: function (v, k) { + + var m = v.length - 1; + var f = m * k; + var i = Math.floor(f); + var fn = TWEEN.Interpolation.Utils.CatmullRom; + + if (v[0] === v[m]) { + + if (k < 0) { + i = Math.floor(f = m * (1 + k)); + } + + return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i); + + } else { + + if (k < 0) { + return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]); + } + + if (k > 1) { + return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]); + } + + return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i); + + } + + }, + + Utils: { + + Linear: function (p0, p1, t) { + + return (p1 - p0) * t + p0; + + }, + + Bernstein: function (n, i) { + + var fc = TWEEN.Interpolation.Utils.Factorial; + + return fc(n) / fc(i) / fc(n - i); + + }, + + Factorial: (function () { + + var a = [1]; + + return function (n) { + + var s = 1; + + if (a[n]) { + return a[n]; + } + + for (var i = n; i > 1; i--) { + s *= i; + } + + a[n] = s; + return s; + + }; + + })(), + + CatmullRom: function (p0, p1, p2, p3, t) { + + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + var t2 = t * t; + var t3 = t * t2; + + return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (- 3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; + + } + + } + +}; + +// UMD (Universal Module Definition) +(function (root) { + + if (typeof define === 'function' && define.amd) { + + // AMD + define([], function () { + return TWEEN; + }); + + } else if (typeof exports === 'object') { + + // Node.js + module.exports = TWEEN; + + } else { + + // Global variable + root.TWEEN = TWEEN; + + } + +})(this); + +loadTween = function(){ + return TWEEN +} \ No newline at end of file From 77e21b7190ab2f999c4ff7bd9f1ed6b421ccd8d1 Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 9 Nov 2015 18:18:30 -0800 Subject: [PATCH 0801/1003] Trying to fix the animation recording and playback, but still not good --- libraries/avatars/src/Player.cpp | 41 +++++++++++++++++++---- libraries/avatars/src/Recorder.cpp | 7 ++-- libraries/avatars/src/Recording.cpp | 51 ++++++++++++++++++++++++++--- libraries/avatars/src/Recording.h | 5 +++ 4 files changed, 91 insertions(+), 13 deletions(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index d3a7124226..5ffaaf6c7b 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -247,22 +247,50 @@ void Player::play() { _frameInterpolationFactor); _avatar->setTargetScale(context->scale * scale); - QVector jointRotations(currentFrame.getJointRotations().size()); + const auto& prevJointArray = currentFrame.getJointArray(); + const auto& nextJointArray = currentFrame.getJointArray(); + QVector jointArray(prevJointArray.size()); + QVector jointRotations(prevJointArray.size()); + QVector jointTranslations(prevJointArray.size()); + + for (int i = 0; i < jointArray.size(); i++) { + const auto& prevJoint = prevJointArray[i]; + const auto& nextJoint = nextJointArray[i]; + auto& joint = jointArray[i]; + + // Rotation + joint.rotationSet = prevJoint.rotationSet || nextJoint.rotationSet; + if (joint.rotationSet) { + joint.rotation = safeMix(prevJoint.rotation, nextJoint.rotation, _frameInterpolationFactor); + jointRotations[i] = joint.rotation; + } + + joint.translationSet = prevJoint.translationSet || nextJoint.translationSet; + if (joint.translationSet) { + joint.translation = glm::mix(prevJoint.translation, nextJoint.translation, _frameInterpolationFactor); + jointTranslations[i] = joint.translation; + } + } + // _avatar->setRawJointData(jointArray); + _avatar->setJointRotations(jointRotations); + // _avatar->setJointTranslations(jointTranslations); + +/* QVector jointRotations(currentFrame.getJointRotations().size()); for (int i = 0; i < currentFrame.getJointRotations().size(); ++i) { jointRotations[i] = safeMix(currentFrame.getJointRotations()[i], nextFrame.getJointRotations()[i], _frameInterpolationFactor); } - - QVector jointTranslations(currentFrame.getJointTranslations().size()); + */ + /* QVector jointTranslations(currentFrame.getJointTranslations().size()); for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) { jointTranslations[i] = glm::mix(currentFrame.getJointTranslations()[i], nextFrame.getJointTranslations()[i], _frameInterpolationFactor); } - - _avatar->setJointRotations(jointRotations); - _avatar->setJointTranslations(jointTranslations); + */ + // _avatar->setJointRotations(jointRotations); + // _avatar->setJointTranslations(jointTranslations); HeadData* head = const_cast(_avatar->getHeadData()); if (head) { @@ -422,3 +450,4 @@ bool Player::computeCurrentFrame() { } return true; } + diff --git a/libraries/avatars/src/Recorder.cpp b/libraries/avatars/src/Recorder.cpp index 68e667604b..6da6596ab9 100644 --- a/libraries/avatars/src/Recorder.cpp +++ b/libraries/avatars/src/Recorder.cpp @@ -102,8 +102,11 @@ void Recorder::record() { frame.setBlendshapeCoefficients(_avatar->getHeadData()->getBlendshapeCoefficients()); // FIXME: here we need to make sure the correct joint data on the AvatarData to get correct play back. // This should be fixed by a fix coming from Howard soon - frame.setJointRotations(_avatar->::AvatarData::getJointRotations()); - frame.setJointTranslations(_avatar->::AvatarData::getJointTranslations()); + auto& jointData = _avatar->getRawJointData(); + + frame.setJointArray(jointData); + // frame.setJointRotations(_avatar->::AvatarData::getJointRotations()); + // frame.setJointTranslations(_avatar->::AvatarData::getJointTranslations()); frame.setTranslation(context.orientationInv * (_avatar->getPosition() - context.position)); frame.setRotation(context.orientationInv * _avatar->getOrientation()); diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 4ca56421e5..a7d1cd5e86 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -229,7 +229,28 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { ++maskIndex; } - // Joint Rotations + const auto& jointArray = frame.getJointArray(); + if (i == 0) { + numJoints = jointArray.size(); + stream << numJoints; + // 2 fields per joints + mask.resize(mask.size() + numJoints * 2); + } + for (int j = 0; j < numJoints; j++) { + const auto& joint = jointArray[j]; + if (joint.rotationSet) { + writeQuat(stream, joint.rotation); + mask.setBit(maskIndex); + } + maskIndex++; + if (joint.translationSet) { + writeVec3(stream, joint.translation); + mask.setBit(maskIndex); + } + maskIndex++; + } + + /* // Joint Rotations if (i == 0) { numJoints = frame.getJointRotations().size(); stream << numJoints; @@ -252,7 +273,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { mask.setBit(maskIndex); } maskIndex++; - } + } */ // Translation if (i == 0) { @@ -561,10 +582,29 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString stream >> frame._blendshapeCoefficients[j]; } } - // Joint Rotations + // Joint Array if (i == 0) { stream >> numJoints; } + + frame._jointArray.resize(numJoints); + for (quint32 j = 0; j < numJoints; ++j) { + auto& joint = frame._jointArray[2]; + + if (mask[maskIndex++] && readQuat(stream, joint.rotation)) { + joint.rotationSet = true; + } else { + joint.rotationSet = false; + } + + if (mask[maskIndex++] || readVec3(stream, joint.translation)) { + joint.translationSet = true; + } else { + joint.translationSet = false; + } + } + + /* frame._jointRotations.resize(numJoints); for (quint32 j = 0; j < numJoints; ++j) { if (!mask[maskIndex++] || !readQuat(stream, frame._jointRotations[j])) { @@ -573,13 +613,14 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } // Joint Translations - frame._jointTranslations.resize(numJoints); + /*frame._jointTranslations.resize(numJoints); for (quint32 j = 0; j < numJoints; ++j) { if (!mask[maskIndex++] || !readVec3(stream, frame._jointTranslations[j])) { frame._jointTranslations[j] = previousFrame._jointTranslations[j]; } } - + */ + if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) { frame._translation = previousFrame._translation; } diff --git a/libraries/avatars/src/Recording.h b/libraries/avatars/src/Recording.h index 3533af6535..83d3642fdd 100644 --- a/libraries/avatars/src/Recording.h +++ b/libraries/avatars/src/Recording.h @@ -25,6 +25,7 @@ class AttachmentData; class Recording; class RecordingFrame; class Sound; +class JointData; typedef QSharedPointer RecordingPointer; @@ -82,6 +83,7 @@ private: class RecordingFrame { public: QVector getBlendshapeCoefficients() const { return _blendshapeCoefficients; } + QVector getJointArray() const { return _jointArray; } QVector getJointRotations() const { return _jointRotations; } QVector getJointTranslations() const { return _jointTranslations; } glm::vec3 getTranslation() const { return _translation; } @@ -94,6 +96,7 @@ public: protected: void setBlendshapeCoefficients(QVector blendshapeCoefficients); + void setJointArray(const QVector& jointArray) { _jointArray = jointArray; } void setJointRotations(QVector jointRotations) { _jointRotations = jointRotations; } void setJointTranslations(QVector jointTranslations) { _jointTranslations = jointTranslations; } void setTranslation(const glm::vec3& translation) { _translation = translation; } @@ -108,6 +111,8 @@ private: QVector _blendshapeCoefficients; QVector _jointRotations; QVector _jointTranslations; + QVector _jointArray; + glm::vec3 _translation; glm::quat _rotation; float _scale; From 1ec70e81705cd0130a8c4bcf9f7d505c7dbea505 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 9 Nov 2015 19:10:11 -0800 Subject: [PATCH 0802/1003] add physics to the ball and make it bounce --- examples/libraries/easyStarExample.js | 64 ++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/examples/libraries/easyStarExample.js b/examples/libraries/easyStarExample.js index eb758a2fe3..c4368f8ff8 100644 --- a/examples/libraries/easyStarExample.js +++ b/examples/libraries/easyStarExample.js @@ -28,10 +28,12 @@ var grid = [ [0, 0, 0, 0, 0, 0, 0, 0, 0] ]; + + easystar.setGrid(grid); easystar.setAcceptableTiles([0]); - +easystar.enableCornerCutting(); easystar.findPath(0, 0, 8, 0, function(path) { if (path === null) { print("Path was not found."); @@ -67,9 +69,17 @@ var playerSphere = Entities.addEntity({ x: 0, y: 0, z: 0 - } -}) + }, + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + collisionsWillMove: true, + linearDamping: 0.2 +}); +var sphereProperties; //for keeping track of entities var obstacles = []; @@ -99,13 +109,15 @@ function createPassableAtTilePosition(posX, posY) { passables.push(passable); } + + function createObstacleAtTilePosition(posX, posY) { var properties = { type: 'Box', shapeType: 'Box', dimensions: { x: 1, - y: 1, + y: 2, z: 1 }, position: { @@ -152,7 +164,6 @@ var currentSpherePosition = { function convertPathPointToCoordinates(x, y) { return { x: y, - y: 0, z: x }; } @@ -169,22 +180,44 @@ function convertPath(path) { } function updatePosition() { + sphereProperties = Entities.getEntityProperties(playerSphere, "position"); + Entities.editEntity(playerSphere, { position: { x: currentSpherePosition.z, - y: currentSpherePosition.y, + y: sphereProperties.position.y, z: currentSpherePosition.x + }, + velocity: { + x: 0, + y: velocityShaper.y, + z: 0 } }); } +var upVelocity = { + x: 0, + y: 2.5, + z: 0 +} + +var noVelocity = { + x: 0, + y: -3.5, + z: 0 +} + function createTweenPath(convertedPath) { var i; var stepTweens = []; + var velocityTweens = []; //create the tweens for (i = 0; i < convertedPath.length - 1; i++) { var stepTween = new TWEEN.Tween(currentSpherePosition).to(convertedPath[i + 1], ANIMATION_DURATION).onUpdate(updatePosition).onComplete(tweenStep); + + stepTweens.push(stepTween); } @@ -194,8 +227,25 @@ function createTweenPath(convertedPath) { stepTweens[j].chain(stepTweens[j + 1]); } - //start the tween + + var velocityUpTween = new TWEEN.Tween(velocityShaper).to(upVelocity, ANIMATION_DURATION).onUpdate(updatePosition); + var velocityDownTween = new TWEEN.Tween(velocityShaper).to(noVelocity, ANIMATION_DURATION).onUpdate(updatePosition); + + velocityUpTween.chain(velocityDownTween); + velocityDownTween.chain(velocityUpTween); + + + velocityUpTween.easing(TWEEN.Easing.Linear.None) + velocityDownTween.easing(TWEEN.Easing.Back.InOut) + //start the tween stepTweens[0].start(); + velocityUpTween.start(); +} + +var velocityShaper = { + x: 0, + y: 0, + z: 0 } function tweenStep() { From 789f3231e10dbe705e20996dcf88dc9110630e83 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 9 Nov 2015 19:18:19 -0800 Subject: [PATCH 0803/1003] easier bouncing --- examples/libraries/easyStarExample.js | 32 ++++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/examples/libraries/easyStarExample.js b/examples/libraries/easyStarExample.js index c4368f8ff8..20a50d2f7d 100644 --- a/examples/libraries/easyStarExample.js +++ b/examples/libraries/easyStarExample.js @@ -29,7 +29,6 @@ var grid = [ ]; - easystar.setGrid(grid); easystar.setAcceptableTiles([0]); @@ -40,6 +39,7 @@ easystar.findPath(0, 0, 8, 0, function(path) { Script.update.disconnect(tickEasyStar); } else { print('path' + JSON.stringify(path)); + convertPath(path); Script.update.disconnect(tickEasyStar); } @@ -79,6 +79,16 @@ var playerSphere = Entities.addEntity({ linearDamping: 0.2 }); +Script.setInterval(function(){ + Entities.editEntity(playerSphere,{ + velocity:{ + x:0, + y:4, + z:0 + } + }) +},1000) + var sphereProperties; //for keeping track of entities @@ -187,12 +197,8 @@ function updatePosition() { x: currentSpherePosition.z, y: sphereProperties.position.y, z: currentSpherePosition.x - }, - velocity: { - x: 0, - y: velocityShaper.y, - z: 0 } + }); } @@ -227,19 +233,9 @@ function createTweenPath(convertedPath) { stepTweens[j].chain(stepTweens[j + 1]); } - - var velocityUpTween = new TWEEN.Tween(velocityShaper).to(upVelocity, ANIMATION_DURATION).onUpdate(updatePosition); - var velocityDownTween = new TWEEN.Tween(velocityShaper).to(noVelocity, ANIMATION_DURATION).onUpdate(updatePosition); - - velocityUpTween.chain(velocityDownTween); - velocityDownTween.chain(velocityUpTween); - - - velocityUpTween.easing(TWEEN.Easing.Linear.None) - velocityDownTween.easing(TWEEN.Easing.Back.InOut) - //start the tween + //start the tween stepTweens[0].start(); - velocityUpTween.start(); + } var velocityShaper = { From b47c5dbff2f8d1d20e3378014d99efa6cf3c8fd0 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 9 Nov 2015 23:12:04 -0800 Subject: [PATCH 0804/1003] Cleaning the code to remove the old rec format and the use of separate joint Rotationns/Translations arrays --- libraries/avatars/src/Player.cpp | 33 ++--- libraries/avatars/src/Recorder.cpp | 10 +- libraries/avatars/src/Recording.cpp | 212 +--------------------------- libraries/avatars/src/Recording.h | 7 - 4 files changed, 14 insertions(+), 248 deletions(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 5ffaaf6c7b..238753a217 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -247,11 +247,14 @@ void Player::play() { _frameInterpolationFactor); _avatar->setTargetScale(context->scale * scale); + // Joint array playback + // FIXME: THis is still using a deprecated path to assign the joint orientation since setting the full RawJointData array doesn't + // work for Avatar. We need to fix this working with the animation team const auto& prevJointArray = currentFrame.getJointArray(); const auto& nextJointArray = currentFrame.getJointArray(); QVector jointArray(prevJointArray.size()); - QVector jointRotations(prevJointArray.size()); - QVector jointTranslations(prevJointArray.size()); + QVector jointRotations(prevJointArray.size()); // FIXME: remove once the setRawJointData is fixed + QVector jointTranslations(prevJointArray.size()); // FIXME: remove once the setRawJointData is fixed for (int i = 0; i < jointArray.size(); i++) { const auto& prevJoint = prevJointArray[i]; @@ -262,35 +265,19 @@ void Player::play() { joint.rotationSet = prevJoint.rotationSet || nextJoint.rotationSet; if (joint.rotationSet) { joint.rotation = safeMix(prevJoint.rotation, nextJoint.rotation, _frameInterpolationFactor); - jointRotations[i] = joint.rotation; + jointRotations[i] = joint.rotation; // FIXME: remove once the setRawJointData is fixed } joint.translationSet = prevJoint.translationSet || nextJoint.translationSet; if (joint.translationSet) { joint.translation = glm::mix(prevJoint.translation, nextJoint.translation, _frameInterpolationFactor); - jointTranslations[i] = joint.translation; + jointTranslations[i] = joint.translation; // FIXME: remove once the setRawJointData is fixed } } - // _avatar->setRawJointData(jointArray); - _avatar->setJointRotations(jointRotations); - // _avatar->setJointTranslations(jointTranslations); -/* QVector jointRotations(currentFrame.getJointRotations().size()); - for (int i = 0; i < currentFrame.getJointRotations().size(); ++i) { - jointRotations[i] = safeMix(currentFrame.getJointRotations()[i], - nextFrame.getJointRotations()[i], - _frameInterpolationFactor); - } - */ - /* QVector jointTranslations(currentFrame.getJointTranslations().size()); - for (int i = 0; i < currentFrame.getJointTranslations().size(); ++i) { - jointTranslations[i] = glm::mix(currentFrame.getJointTranslations()[i], - nextFrame.getJointTranslations()[i], - _frameInterpolationFactor); - } - */ - // _avatar->setJointRotations(jointRotations); - // _avatar->setJointTranslations(jointTranslations); + // _avatar->setRawJointData(jointArray); // FIXME: Enable once the setRawJointData is fixed + _avatar->setJointRotations(jointRotations); // FIXME: remove once the setRawJointData is fixed + _avatar->setJointTranslations(jointTranslations); // FIXME: remove once the setRawJointData is fixed HeadData* head = const_cast(_avatar->getHeadData()); if (head) { diff --git a/libraries/avatars/src/Recorder.cpp b/libraries/avatars/src/Recorder.cpp index 6da6596ab9..5e47c296eb 100644 --- a/libraries/avatars/src/Recorder.cpp +++ b/libraries/avatars/src/Recorder.cpp @@ -100,13 +100,10 @@ void Recorder::record() { const RecordingContext& context = _recording->getContext(); RecordingFrame frame; frame.setBlendshapeCoefficients(_avatar->getHeadData()->getBlendshapeCoefficients()); - // FIXME: here we need to make sure the correct joint data on the AvatarData to get correct play back. - // This should be fixed by a fix coming from Howard soon - auto& jointData = _avatar->getRawJointData(); + // Capture the full skeleton joint data + auto& jointData = _avatar->getRawJointData(); frame.setJointArray(jointData); - // frame.setJointRotations(_avatar->::AvatarData::getJointRotations()); - // frame.setJointTranslations(_avatar->::AvatarData::getJointTranslations()); frame.setTranslation(context.orientationInv * (_avatar->getPosition() - context.position)); frame.setRotation(context.orientationInv * _avatar->getOrientation()); @@ -129,8 +126,7 @@ void Recorder::record() { if (wantDebug) { qCDebug(avatars) << "Recording frame #" << _recording->getFrameNumber(); qCDebug(avatars) << "Blendshapes:" << frame.getBlendshapeCoefficients().size(); - qCDebug(avatars) << "JointRotations:" << frame.getJointRotations().size(); - qCDebug(avatars) << "JointRotations:" << frame.getJointTranslations().size(); + qCDebug(avatars) << "JointArray:" << frame.getJointArray().size(); qCDebug(avatars) << "Translation:" << frame.getTranslation(); qCDebug(avatars) << "Rotation:" << frame.getRotation(); qCDebug(avatars) << "Scale:" << frame.getScale(); diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index a7d1cd5e86..dd9c722e26 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -250,31 +250,6 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { maskIndex++; } - /* // Joint Rotations - if (i == 0) { - numJoints = frame.getJointRotations().size(); - stream << numJoints; - mask.resize(mask.size() + numJoints); - } - for (quint32 j = 0; j < numJoints; ++j) { - if (i == 0 || - frame._jointRotations[j] != previousFrame._jointRotations[j]) { - writeQuat(stream, frame._jointRotations[j]); - mask.setBit(maskIndex); - } - maskIndex++; - } - - // Joint Translations - for (quint32 j = 0; j < numJoints; ++j) { - if (i == 0 || - frame._jointTranslations[j] != previousFrame._jointTranslations[j]) { - writeVec3(stream, frame._jointTranslations[j]); - mask.setBit(maskIndex); - } - maskIndex++; - } */ - // Translation if (i == 0) { mask.resize(mask.size() + 1); @@ -438,11 +413,7 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString file.close(); } - if (filename.endsWith(".rec") || filename.endsWith(".REC")) { - qCDebug(avatars) << "Old .rec format"; - readRecordingFromRecFile(recording, filename, byteArray); - return recording; - } else if (!filename.endsWith(".hfr") && !filename.endsWith(".HFR")) { + if (!filename.endsWith(".hfr") && !filename.endsWith(".HFR")) { qCDebug(avatars) << "File extension not recognized"; } @@ -604,23 +575,6 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString } } - /* - frame._jointRotations.resize(numJoints); - for (quint32 j = 0; j < numJoints; ++j) { - if (!mask[maskIndex++] || !readQuat(stream, frame._jointRotations[j])) { - frame._jointRotations[j] = previousFrame._jointRotations[j]; - } - } - - // Joint Translations - /*frame._jointTranslations.resize(numJoints); - for (quint32 j = 0; j < numJoints; ++j) { - if (!mask[maskIndex++] || !readVec3(stream, frame._jointTranslations[j])) { - frame._jointTranslations[j] = previousFrame._jointTranslations[j]; - } - } - */ - if (!mask[maskIndex++] || !readVec3(stream, frame._translation)) { frame._translation = previousFrame._translation; } @@ -705,167 +659,3 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString return recording; } - -RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray) { - QElapsedTimer timer; - timer.start(); - - if (!recording) { - recording = QSharedPointer::create(); - } - - QDataStream fileStream(byteArray); - - fileStream >> recording->_timestamps; - RecordingFrame baseFrame; - - // Blendshape coefficients - fileStream >> baseFrame._blendshapeCoefficients; - - // Joint Rotations - int jointRotationSize; - fileStream >> jointRotationSize; - baseFrame._jointRotations.resize(jointRotationSize); - for (int i = 0; i < jointRotationSize; ++i) { - fileStream >> baseFrame._jointRotations[i].x >> baseFrame._jointRotations[i].y >> baseFrame._jointRotations[i].z >> baseFrame._jointRotations[i].w; - } - - // TODO -- handle translations - - fileStream >> baseFrame._translation.x >> baseFrame._translation.y >> baseFrame._translation.z; - fileStream >> baseFrame._rotation.x >> baseFrame._rotation.y >> baseFrame._rotation.z >> baseFrame._rotation.w; - fileStream >> baseFrame._scale; - fileStream >> baseFrame._headRotation.x >> baseFrame._headRotation.y >> baseFrame._headRotation.z >> baseFrame._headRotation.w; - fileStream >> baseFrame._leanSideways; - fileStream >> baseFrame._leanForward; - - - // Fake context - RecordingContext& context = recording->getContext(); - context.globalTimestamp = usecTimestampNow(); - context.domain = DependencyManager::get()->getDomainHandler().getHostname(); - context.position = glm::vec3(144.5f, 3.3f, 181.3f); - context.orientation = glm::angleAxis(glm::radians(-92.5f), glm::vec3(0, 1, 0));; - context.scale = baseFrame._scale; - context.headModel = "http://public.highfidelity.io/models/heads/Emily_v4.fst"; - context.skeletonModel = "http://public.highfidelity.io/models/skeletons/EmilyCutMesh_A.fst"; - context.displayName = "Leslie"; - context.attachments.clear(); - AttachmentData data; - data.modelURL = "http://public.highfidelity.io/models/attachments/fbx.fst"; - data.jointName = "RightHand" ; - data.translation = glm::vec3(0.04f, 0.07f, 0.0f); - data.rotation = glm::angleAxis(glm::radians(102.0f), glm::vec3(0, 1, 0)); - data.scale = 0.20f; - context.attachments << data; - - context.orientationInv = glm::inverse(context.orientation); - - baseFrame._translation = glm::vec3(); - baseFrame._rotation = glm::quat(); - baseFrame._scale = 1.0f; - - recording->_frames << baseFrame; - - for (int i = 1; i < recording->_timestamps.size(); ++i) { - QBitArray mask; - QByteArray buffer; - QDataStream stream(&buffer, QIODevice::ReadOnly); - RecordingFrame frame; - RecordingFrame& previousFrame = recording->_frames.last(); - - fileStream >> mask; - fileStream >> buffer; - int maskIndex = 0; - - // Blendshape Coefficients - frame._blendshapeCoefficients.resize(baseFrame._blendshapeCoefficients.size()); - for (int i = 0; i < baseFrame._blendshapeCoefficients.size(); ++i) { - if (mask[maskIndex++]) { - stream >> frame._blendshapeCoefficients[i]; - } else { - frame._blendshapeCoefficients[i] = previousFrame._blendshapeCoefficients[i]; - } - } - - // Joint Rotations - frame._jointRotations.resize(baseFrame._jointRotations.size()); - for (int i = 0; i < baseFrame._jointRotations.size(); ++i) { - if (mask[maskIndex++]) { - stream >> frame._jointRotations[i].x >> frame._jointRotations[i].y >> frame._jointRotations[i].z >> frame._jointRotations[i].w; - } else { - frame._jointRotations[i] = previousFrame._jointRotations[i]; - } - } - - // TODO -- handle translations - - if (mask[maskIndex++]) { - stream >> frame._translation.x >> frame._translation.y >> frame._translation.z; - frame._translation = context.orientationInv * frame._translation; - } else { - frame._translation = previousFrame._translation; - } - - if (mask[maskIndex++]) { - stream >> frame._rotation.x >> frame._rotation.y >> frame._rotation.z >> frame._rotation.w; - } else { - frame._rotation = previousFrame._rotation; - } - - if (mask[maskIndex++]) { - stream >> frame._scale; - } else { - frame._scale = previousFrame._scale; - } - - if (mask[maskIndex++]) { - stream >> frame._headRotation.x >> frame._headRotation.y >> frame._headRotation.z >> frame._headRotation.w; - } else { - frame._headRotation = previousFrame._headRotation; - } - - if (mask[maskIndex++]) { - stream >> frame._leanSideways; - } else { - frame._leanSideways = previousFrame._leanSideways; - } - - if (mask[maskIndex++]) { - stream >> frame._leanForward; - } else { - frame._leanForward = previousFrame._leanForward; - } - - recording->_frames << frame; - } - - QByteArray audioArray; - fileStream >> audioArray; - - // Cut down audio if necessary - int SAMPLE_SIZE = 2; // 16 bits - int MSEC_PER_SEC = 1000; - int audioLength = recording->getLength() * SAMPLE_SIZE * (AudioConstants::SAMPLE_RATE / MSEC_PER_SEC); - audioArray.chop(audioArray.size() - audioLength); - - recording->addAudioPacket(audioArray); - - qCDebug(avatars) << "Read " << byteArray.size() << " bytes in " << timer.elapsed() << " ms."; - - // Set new filename - QString newFilename = filename; - if (newFilename.startsWith("http") || newFilename.startsWith("https") || newFilename.startsWith("ftp")) { - newFilename = QUrl(newFilename).fileName(); - } - if (newFilename.endsWith(".rec") || newFilename.endsWith(".REC")) { - newFilename.chop(qstrlen(".rec")); - } - newFilename.append(".hfr"); - newFilename = QFileInfo(newFilename).absoluteFilePath(); - - // Set recording to new format - writeRecordingToFile(recording, newFilename); - qCDebug(avatars) << "Recording has been successfully converted at" << newFilename; - return recording; -} diff --git a/libraries/avatars/src/Recording.h b/libraries/avatars/src/Recording.h index 83d3642fdd..7657a12b46 100644 --- a/libraries/avatars/src/Recording.h +++ b/libraries/avatars/src/Recording.h @@ -84,8 +84,6 @@ class RecordingFrame { public: QVector getBlendshapeCoefficients() const { return _blendshapeCoefficients; } QVector getJointArray() const { return _jointArray; } - QVector getJointRotations() const { return _jointRotations; } - QVector getJointTranslations() const { return _jointTranslations; } glm::vec3 getTranslation() const { return _translation; } glm::quat getRotation() const { return _rotation; } float getScale() const { return _scale; } @@ -97,8 +95,6 @@ public: protected: void setBlendshapeCoefficients(QVector blendshapeCoefficients); void setJointArray(const QVector& jointArray) { _jointArray = jointArray; } - void setJointRotations(QVector jointRotations) { _jointRotations = jointRotations; } - void setJointTranslations(QVector jointTranslations) { _jointTranslations = jointTranslations; } void setTranslation(const glm::vec3& translation) { _translation = translation; } void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setScale(float scale) { _scale = scale; } @@ -109,8 +105,6 @@ protected: private: QVector _blendshapeCoefficients; - QVector _jointRotations; - QVector _jointTranslations; QVector _jointArray; glm::vec3 _translation; @@ -130,6 +124,5 @@ private: void writeRecordingToFile(RecordingPointer recording, const QString& filename); RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString& filename); -RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray); #endif // hifi_Recording_h From 06a87012ad63567d5d1c6b07138d10cad682735a Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 9 Nov 2015 23:38:51 -0800 Subject: [PATCH 0805/1003] Removing the setJointTranslations which is not helping... --- libraries/avatars/src/Player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 238753a217..47fc1390d9 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -277,7 +277,7 @@ void Player::play() { // _avatar->setRawJointData(jointArray); // FIXME: Enable once the setRawJointData is fixed _avatar->setJointRotations(jointRotations); // FIXME: remove once the setRawJointData is fixed - _avatar->setJointTranslations(jointTranslations); // FIXME: remove once the setRawJointData is fixed + // _avatar->setJointTranslations(jointTranslations); // FIXME: remove once the setRawJointData is fixed HeadData* head = const_cast(_avatar->getHeadData()); if (head) { From 0ea901fd721081dcfcd969bdd928da007666b3f6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 10 Nov 2015 08:34:58 -0800 Subject: [PATCH 0806/1003] fix various warnings --- .../src/RenderableParticleEffectEntityItem.cpp | 1 - libraries/recording/src/recording/Deck.cpp | 5 ++++- libraries/recording/src/recording/Frame.cpp | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 6f3b40eb1e..41cf3b9bbf 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -218,7 +218,6 @@ void RenderableParticleEffectEntityItem::updateRenderItem() { _vertices.clear(); // build vertices from particle positions and radiuses - glm::vec3 frustumPosition = frustum->getPosition(); glm::vec3 dir = frustum->getDirection(); for (auto&& particle : particleDetails) { glm::vec3 right = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), dir)); diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index a80fc43204..f0db37078b 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -6,4 +6,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Deck.h" + +// FIXME -- DO NOT include headers in empty CPP files, it produces warnings. Once we define new symbols +// and some actual code here, we can uncomment this include. +//#include "Deck.h" diff --git a/libraries/recording/src/recording/Frame.cpp b/libraries/recording/src/recording/Frame.cpp index 211f192c0a..aac8a4d9c3 100644 --- a/libraries/recording/src/recording/Frame.cpp +++ b/libraries/recording/src/recording/Frame.cpp @@ -80,6 +80,7 @@ FrameType Frame::registerFrameType(const QString& frameTypeName) { std::call_once(once, [&] { auto headerType = frameTypes.registerValue("com.highfidelity.recording.Header"); Q_ASSERT(headerType == Frame::TYPE_HEADER); + Q_UNUSED(headerType); // FIXME - build system on unix still not upgraded to Qt 5.5.1 so Q_ASSERT still produces warnings }); return frameTypes.registerValue(frameTypeName); } From 4a0a481ac6c654b205e4b7d43e6586ddc5fdf66b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 10 Nov 2015 08:44:51 -0800 Subject: [PATCH 0807/1003] more fixes --- tests/recording/src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/recording/src/main.cpp b/tests/recording/src/main.cpp index 96c9a7f1a7..f4049b04b7 100644 --- a/tests/recording/src/main.cpp +++ b/tests/recording/src/main.cpp @@ -94,6 +94,7 @@ void testClipOrdering() { for (auto writeFrame = writeClip->nextFrame(); writeFrame; writeFrame = writeClip->nextFrame()) { QVERIFY(writeClip->position() >= lastFrameTimeOffset); } + Q_UNUSED(lastFrameTimeOffset); // FIXME - Unix build not yet upgraded to Qt 5.5.1 we can remove this once it is } #ifdef Q_OS_WIN32 From 53ca4c857d8d10752b9b0c6f82b55bd8ce01bcfb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 08:52:37 -0800 Subject: [PATCH 0808/1003] Fix relative include path in entityCameraTool.js --- examples/libraries/entityCameraTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/entityCameraTool.js b/examples/libraries/entityCameraTool.js index 88e01b29fe..e3e86cedb3 100644 --- a/examples/libraries/entityCameraTool.js +++ b/examples/libraries/entityCameraTool.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/overlayUtils.js"); +Script.include("overlayUtils.js"); var MOUSE_SENSITIVITY = 0.9; var SCROLL_SENSITIVITY = 0.05; From 2cf2257783198ab14435347ca098871396258268 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 10 Nov 2015 09:20:43 -0800 Subject: [PATCH 0809/1003] Fix warning --- libraries/avatars/src/Recording.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index dd9c722e26..fa343630ed 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -236,7 +236,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { // 2 fields per joints mask.resize(mask.size() + numJoints * 2); } - for (int j = 0; j < numJoints; j++) { + for (quint32 j = 0; j < numJoints; j++) { const auto& joint = jointArray[j]; if (joint.rotationSet) { writeQuat(stream, joint.rotation); From f5308530e680641260250b7fb55d2b475d36dec2 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 10 Nov 2015 09:35:35 -0800 Subject: [PATCH 0810/1003] Removed commented out line --- examples/helicopter/helicopter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/helicopter/helicopter.js b/examples/helicopter/helicopter.js index c1ad0f9d30..7073722e1e 100644 --- a/examples/helicopter/helicopter.js +++ b/examples/helicopter/helicopter.js @@ -51,7 +51,6 @@ var helicopter = Entities.addEntity({ y: 3.14, z: 9.92 }, - // rotation: Quat.fromPitchYawRollDegrees(0, -90, 0), position: spawnPosition, }); From b802da4fef56d06638b863843b3519f1a1865b89 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 09:37:36 -0800 Subject: [PATCH 0811/1003] Fix include in satellite.js --- examples/example/games/satellite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example/games/satellite.js b/examples/example/games/satellite.js index db65198b87..9ae0105917 100644 --- a/examples/example/games/satellite.js +++ b/examples/example/games/satellite.js @@ -15,7 +15,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include('../utilities/tools/vector.js'); +Script.include('../../utilities/tools/vector.js'); var URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/"; From 37d9d98594681c92a22c5d7b1fc6d0d224a98244 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 09:37:53 -0800 Subject: [PATCH 0812/1003] Fix breakdance include paths --- .../libraries/omniTool/modules/breakdanceOmniToolModule.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js b/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js index 36ee6b1fee..3764c5e381 100644 --- a/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js +++ b/examples/libraries/omniTool/modules/breakdanceOmniToolModule.js @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("../toys/breakdanceCore.js"); +Script.include("../../../breakdanceCore.js"); OmniToolModules.Breakdance = function() { print("OmniToolModules.Breakdance..."); @@ -32,4 +32,4 @@ OmniToolModules.Breakdance.prototype.onUpdate = function(deltaTime) { breakdanceEnd(); } -OmniToolModuleType = "Breakdance"; \ No newline at end of file +OmniToolModuleType = "Breakdance"; From 29fd352169427959c315323f43fe295ac5ea62f6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 09:38:21 -0800 Subject: [PATCH 0813/1003] Fix omniTool test module include path --- examples/libraries/omniTool/modules/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/omniTool/modules/test.js b/examples/libraries/omniTool/modules/test.js index 9f7191b2d0..83472ee0f1 100644 --- a/examples/libraries/omniTool/modules/test.js +++ b/examples/libraries/omniTool/modules/test.js @@ -1,5 +1,5 @@ -Script.include("avatarRelativeOverlays.js"); +Script.include("../../avatarRelativeOverlays.js"); OmniToolModules.Test = function(omniTool, activeEntityId) { this.omniTool = omniTool; From 2ad4c5b9e22118843b100fcfaed24b78f09ea6e9 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 09:38:40 -0800 Subject: [PATCH 0814/1003] Fix walkApi include paths --- examples/libraries/walkApi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/libraries/walkApi.js b/examples/libraries/walkApi.js index 8935380150..3a51491cac 100644 --- a/examples/libraries/walkApi.js +++ b/examples/libraries/walkApi.js @@ -14,7 +14,7 @@ // // included here to ensure walkApi.js can be used as an API, separate from walk.js -Script.include("./libraries/walkConstants.js"); +Script.include("walkConstants.js"); Avatar = function() { // if Hydras are connected, the only way to enable use is to never set any arm joint rotation From 6577c7a13c792632e8f9b558033cde8cbf8acb9c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 09:38:52 -0800 Subject: [PATCH 0815/1003] Fix magBalls include paths --- examples/magBalls.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/magBalls.js b/examples/magBalls.js index c9b45164ab..465e5f8e57 100644 --- a/examples/magBalls.js +++ b/examples/magBalls.js @@ -7,11 +7,11 @@ // // FIXME Script paths have to be relative to the caller, in this case libraries/OmniTool.js -Script.include("../magBalls/constants.js"); -Script.include("../magBalls/graph.js"); -Script.include("../magBalls/edgeSpring.js"); -Script.include("../magBalls/magBalls.js"); -Script.include("avatarRelativeOverlays.js"); +Script.include("magBalls/constants.js"); +Script.include("magBalls/graph.js"); +Script.include("magBalls/edgeSpring.js"); +Script.include("magBalls/magBalls.js"); +Script.include("libraries/avatarRelativeOverlays.js"); OmniToolModuleType = "MagBallsController" From d535109cafbc5b6cfe925a0303c4c18b275a083c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 09:49:31 -0800 Subject: [PATCH 0816/1003] attempt to fix render-collision hull feature --- .../src/RenderableModelEntityItem.cpp | 10 ++++---- .../src/RenderableModelEntityItem.h | 2 ++ .../src/RenderableZoneEntityItem.cpp | 2 +- libraries/render-utils/src/Model.cpp | 24 ++++++++++++++----- libraries/render-utils/src/Model.h | 8 +++++-- libraries/shared/src/RenderArgs.h | 7 +++--- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8543c00eec..d33e52fa6b 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -227,7 +227,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p // note: we don't care if the model fails to add items, we always added our meta item and therefore we return // true so that the system knows our meta item is in the scene! - _model->addToScene(scene, pendingChanges, statusGetters); + _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); } return true; @@ -259,14 +259,16 @@ void RenderableModelEntityItem::render(RenderArgs* args) { // check to see if when we added our models to the scene they were ready, if they were not ready, then // fix them up in the scene - if (_model->needsFixupInScene()) { + bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0; + if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) { + _showCollisionHull = shouldShowCollisionHull; render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; makeEntityItemStatusGetters(this, statusGetters); - _model->addToScene(scene, pendingChanges, statusGetters); + _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); scene->enqueuePendingChanges(pendingChanges); } @@ -288,7 +290,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { EntityTreeRenderer* renderer = static_cast(args->_renderer); getModel(renderer); } - + if (_model) { // handle animations.. if (hasAnimation()) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 04a1694dd3..c4e36c240a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -81,6 +81,8 @@ private: bool _dimensionsInitialized = true; render::ItemID _myMetaItem; + + bool _showCollisionHull = false; }; #endif // hifi_RenderableModelEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index ff56bef46b..62d98f3322 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -112,7 +112,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); - _model->addToScene(scene, pendingChanges); + _model->addToScene(scene, pendingChanges, false); scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6aae7ad1cb..92f057647c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -508,8 +508,10 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr scen } -bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { - if (!_meshGroupsKnown && isLoaded()) { +bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, bool showCollisionHull) { + + if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) { + _showCollisionHull = showCollisionHull; segregateMeshGroups(); } @@ -532,8 +534,12 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan return somethingAdded; } -bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { - if (!_meshGroupsKnown && isLoaded()) { +bool Model::addToScene(std::shared_ptr scene, + render::PendingChanges& pendingChanges, + render::Item::Status::Getters& statusGetters, + bool showCollisionHull) { + if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) { + _showCollisionHull = showCollisionHull; segregateMeshGroups(); } @@ -1144,8 +1150,14 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) { } void Model::segregateMeshGroups() { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const std::vector>& networkMeshes = _geometry->getMeshes(); + QSharedPointer networkGeometry; + if (_showCollisionHull && _collisionGeometry && _collisionGeometry->isLoaded()) { + networkGeometry = _collisionGeometry; + } else { + networkGeometry = _geometry; + } + const FBXGeometry& geometry = networkGeometry->getFBXGeometry(); + const std::vector>& networkMeshes = networkGeometry->getMeshes(); _rig->makeAnimSkeleton(geometry); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f5d5f40363..b154ae2b52 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -76,10 +76,13 @@ public: return !_needsReload && isRenderable() && isActive() && isLoaded(); } bool initWhenReady(render::ScenePointer scene); - bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, - render::Item::Status::Getters& statusGetters); + bool showCollisionHull = false); + bool addToScene(std::shared_ptr scene, + render::PendingChanges& pendingChanges, + render::Item::Status::Getters& statusGetters, + bool showCollisionHull = false); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); void renderSetup(RenderArgs* args); bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().empty()); } @@ -368,6 +371,7 @@ private: bool _readyWhenAdded = false; bool _needsReload = true; bool _needsUpdateClusterMatrices = true; + bool _showCollisionHull = false; friend class MeshPartPayload; protected: diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 25eed96490..fcacf7aaed 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -68,17 +68,16 @@ public: class RenderArgs { public: typedef std::function ShoudRenderFunctor; - + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE }; enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT }; enum DebugFlags { RENDER_DEBUG_NONE = 0, - RENDER_DEBUG_HULLS = 1, - RENDER_DEBUG_SIMULATION_OWNERSHIP = 2, + RENDER_DEBUG_HULLS = 1 }; - + RenderArgs(std::shared_ptr context = nullptr, OctreeRenderer* renderer = nullptr, ViewFrustum* viewFrustum = nullptr, From 8e466190e0f69c122f1bb73d8d8a9282b500a8c6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 10:15:58 -0800 Subject: [PATCH 0817/1003] working toward display icons for debug/status display --- .../render-utils/src/RenderDeferredTask.cpp | 9 +++++++-- libraries/render/src/render/DrawStatus.cpp | 16 ++++++++++++++-- libraries/render/src/render/DrawStatus.h | 11 +++++++++-- libraries/render/src/render/DrawTask.h | 2 +- libraries/render/src/render/Scene.cpp | 5 ++++- libraries/render/src/render/Scene.h | 8 ++++++-- libraries/render/src/render/drawItemStatus.slf | 9 ++++++++- libraries/render/src/render/drawItemStatus.slv | 12 +++++++----- 8 files changed, 56 insertions(+), 16 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 845c96372c..baa4b8f259 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -12,6 +12,7 @@ #include "RenderDeferredTask.h" #include +#include #include #include #include @@ -111,7 +112,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); - _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques))); + // Grab a texture map representing the different status icons and assign that to the drawStatsuJob + auto iconMapPath = PathUtils::resourcesPath() + "images/hifi-logo.svg"; + + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); _jobs.back().setEnabled(false); @@ -387,4 +392,4 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const }); args->_batch = nullptr; -} \ No newline at end of file +} diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 57e21a1511..190370fd48 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -29,7 +29,7 @@ using namespace render; -const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() { +const gpu::PipelinePointer DrawStatus::getDrawItemBoundsPipeline() { if (!_drawItemBoundsPipeline) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert))); auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag))); @@ -56,13 +56,14 @@ const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() { return _drawItemBoundsPipeline; } -const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() { +const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() { if (!_drawItemStatusPipeline) { auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert))); auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag))); gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("iconStatusMap"), 0)); gpu::Shader::makeProgram(*program, slotBindings); _drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos"); @@ -84,6 +85,14 @@ const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() { return _drawItemStatusPipeline; } +void DrawStatus::setStatusIconMap(const gpu::TexturePointer& map) { + _statusIconMap = map; +} + +const gpu::TexturePointer DrawStatus::getStatusIconMap() const { + return _statusIconMap; +} + void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); @@ -151,6 +160,8 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex batch.draw(gpu::LINES, 24, 0); } + batch.setResourceTexture(0, gpu::TextureView(getStatusIconMap(), 0)); + batch.setPipeline(getDrawItemStatusPipeline()); for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); @@ -159,5 +170,6 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex batch.draw(gpu::TRIANGLES, 24, 0); } + batch.setResourceTexture(0, 0); }); } diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index a96b897f5c..bb9cb07e4b 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -28,15 +28,22 @@ namespace render { gpu::PipelinePointer _drawItemStatusPipeline; gpu::BufferPointer _itemBounds; gpu::BufferPointer _itemStatus; + gpu::TexturePointer _statusIconMap; public: + DrawStatus() {} + DrawStatus(const gpu::TexturePointer statusIconMap) { setStatusIconMap(statusIconMap); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); typedef Job::ModelI JobModel; - const gpu::PipelinePointer& getDrawItemBoundsPipeline(); - const gpu::PipelinePointer& getDrawItemStatusPipeline(); + const gpu::PipelinePointer getDrawItemBoundsPipeline(); + const gpu::PipelinePointer getDrawItemStatusPipeline(); + + void setStatusIconMap(const gpu::TexturePointer& map); + const gpu::TexturePointer getStatusIconMap() const; }; } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index ed51273f88..3f628c3a02 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -143,7 +143,7 @@ public: const Varying getInput() const { return _input; } - ModelI(const std::string& name, const Varying& input): Concept(name), _input(input) {} + ModelI(const std::string& name, const Varying& input, Data data = Data()) : Concept(name), _data(data), _input(input) {} ModelI(const std::string& name, Data data): Concept(name), _data(data) {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index fb6782e011..2081d5267f 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -72,7 +72,10 @@ void Item::Status::Value::setScale(float scale) { void Item::Status::Value::setColor(float hue) { // Convert the HUe from range [0, 360] to signed normalized value const float HUE_MAX = 360.0f; - _color = (std::numeric_limits::max() - 1) * 0.5f * (1.0f + std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX); + _color = (std::numeric_limits::max()) * (std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX); +} +void Item::Status::Value::setIcon(unsigned char icon) { + _icon = icon; } void Item::Status::getPackedValues(glm::ivec4& values) const { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index ab71a583b4..459f15055f 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -206,17 +206,21 @@ public: // It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue class Value { unsigned short _scale = 0xFFFF; - unsigned short _color = 0xFFFF; + unsigned char _color = 0xFF; + unsigned char _icon = 0xFF; public: const static Value INVALID; // Invalid value meanss the status won't show Value() {} - Value(float scale, float hue) { setScale(scale); setColor(hue); } + Value(float scale, float hue, unsigned char icon = 0) { setScale(scale); setColor(hue); setIcon(icon); } // It can be scaled in the range [0, 1] void setScale(float scale); // the color hue in the range [0, 360] representing the color wheel hue void setColor(float hue); + // the icon to display in the range [0, 255], where 0 means no icon, just filled quad and anything else would + // hopefully have an icon available to display (see DrawStatusJob) + void setIcon(unsigned char icon); // Standard color Hue static const float RED; // 0.0f; diff --git a/libraries/render/src/render/drawItemStatus.slf b/libraries/render/src/render/drawItemStatus.slf index 4cc45db2ec..3b902e151d 100644 --- a/libraries/render/src/render/drawItemStatus.slf +++ b/libraries/render/src/render/drawItemStatus.slf @@ -12,9 +12,16 @@ // in vec4 varColor; +in vec3 varTexcoord; out vec4 outFragColor; +uniform sampler2D _icons; + void main(void) { - outFragColor = varColor; + if (varTexcoord.z > 0.0f) { + outFragColor = texture(_icons, varTexcoord.xy) * varColor; + } else { + outFragColor = varColor; + } } diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index 178293575d..8a9ca591c7 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -17,6 +17,7 @@ <$declareStandardTransform()$> out vec4 varColor; +out vec3 varTexcoord; uniform vec3 inBoundPos; uniform vec3 inBoundDim; @@ -43,9 +44,10 @@ vec3 paintRainbow(float normalizedHue) { } } -vec2 unpackStatus(int v) { - return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), - clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0)); +vec3 unpackStatus(int v) { + return vec3(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), + clamp(float(uint((v >> 16) & 0xFF)) / 255.0, 0.0, 1.0), + clamp(float(int((v >> 24) & 0xFF)), 0.0, 256.0)); } void main(void) { @@ -78,14 +80,14 @@ void main(void) { } // unpack to get x and y satus - vec2 iconStatus = unpackStatus(inStatus[iconNum]); + vec3 iconStatus = unpackStatus(inStatus[iconNum]); // Use the status for showing a color varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); // Also changes the size of the notification vec2 iconScale = ICON_PIXEL_SIZE; - iconScale = max(vec2(1, 1), (iconScale * iconStatus.x)); + iconScale = max(vec2(0, 0), (iconScale * iconStatus.x)); //Offset icon to the right based on the iconNum vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); From 7f6a20af99b3a118475c2c4dc87a9c507e55b533 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 10 Nov 2015 19:22:57 +0100 Subject: [PATCH 0818/1003] bump entities protocol version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 24034ff9b3..64936c3a59 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_PARTICLES_ADDITIVE_BLENDING; + return VERSION_ENTITIES_ANCHOR; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 82d905bf28..56cb60e02b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -146,5 +146,6 @@ 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; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; +const PacketVersion VERSION_ENTITIES_ANCHOR = 50; #endif // hifi_PacketHeaders_h From e7e6c63c64b8c20520331cff2f2443874c5a6063 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:36:53 -0800 Subject: [PATCH 0819/1003] Fix edit.js resolvePath calls --- examples/libraries/entityList.js | 4 +++- examples/libraries/gridTool.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js index bb84ce27b4..1aa08fbe2d 100644 --- a/examples/libraries/entityList.js +++ b/examples/libraries/entityList.js @@ -1,7 +1,9 @@ +var ENTITY_LIST_HTML_URL = Script.resolvePath('../html/entityList.html'); + EntityListTool = function(opts) { var that = {}; - var url = Script.resolvePath('html/entityList.html'); + var url = ENTITY_LIST_HTML_URL; var webView = new WebWindow('Entities', url, 200, 280, true); var searchRadius = 100; diff --git a/examples/libraries/gridTool.js b/examples/libraries/gridTool.js index ed4e999be8..35d9858ace 100644 --- a/examples/libraries/gridTool.js +++ b/examples/libraries/gridTool.js @@ -1,3 +1,5 @@ +var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html'); + Grid = function(opts) { var that = {}; @@ -228,7 +230,7 @@ GridTool = function(opts) { var verticalGrid = opts.verticalGrid; var listeners = []; - var url = Script.resolvePath('html/gridControls.html'); + var url = GRID_CONTROLS_HTML_URL; var webView = new WebWindow('Grid', url, 200, 280, true); horizontalGrid.addListener(function(data) { From 2a349620eef3c094efbbd383e261209a55a11ebb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:37:59 -0800 Subject: [PATCH 0820/1003] Fix walkSettings.js resolvePath calls --- examples/libraries/walkSettings.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/libraries/walkSettings.js b/examples/libraries/walkSettings.js index 3e5ac53572..0378f305b5 100644 --- a/examples/libraries/walkSettings.js +++ b/examples/libraries/walkSettings.js @@ -13,6 +13,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +var WALK_SETTINGS_HTML_URL = Script.resolvePath('../html/walkSettings.html'); + WalkSettings = function() { var _visible = false; var _innerWidth = Window.innerWidth; @@ -69,7 +71,7 @@ WalkSettings = function() { // web window const PANEL_WIDTH = 200; const PANEL_HEIGHT = 180; - var _url = Script.resolvePath('html/walkSettings.html'); + var _url = WALK_SETTINGS_HTML_URL; var _webWindow = new WebWindow('Walk Settings', _url, PANEL_WIDTH, PANEL_HEIGHT, false); _webWindow.setVisible(false); _webWindow.eventBridge.webEventReceived.connect(function(data) { From 90a1fbe04eecadbf5e7f0db5c19e645ffb8ab73d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:38:16 -0800 Subject: [PATCH 0821/1003] Fix magBalls.js resolvePath calls --- examples/magBalls.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/magBalls.js b/examples/magBalls.js index 465e5f8e57..bda6ccd9c3 100644 --- a/examples/magBalls.js +++ b/examples/magBalls.js @@ -34,7 +34,7 @@ MODE_INFO[BALL_EDIT_MODE_ADD] = { }, colors: [ COLORS.GREEN, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file - url: Script.resolvePath('../html/magBalls/addMode.html'), + url: Script.resolvePath('html/magBalls/addMode.html'), }; MODE_INFO[BALL_EDIT_MODE_DELETE] = { @@ -45,7 +45,7 @@ MODE_INFO[BALL_EDIT_MODE_DELETE] = { }, colors: [ COLORS.RED, COLORS.BLUE ], // FIXME use an http path or find a way to get the relative path to the file - url: Script.resolvePath('../html/magBalls/deleteMode.html'), + url: Script.resolvePath('html/magBalls/deleteMode.html'), }; var UI_POSITION_MODE_LABEL = Vec3.multiply(0.5, From a6bc1a2b5b75d0b7d2abcc6beb73ec47e20045bc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:38:36 -0800 Subject: [PATCH 0822/1003] Fix closePaint.js Script.include --- examples/painting/closePaint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/painting/closePaint.js b/examples/painting/closePaint.js index d9f70aab3c..563ed1dafb 100644 --- a/examples/painting/closePaint.js +++ b/examples/painting/closePaint.js @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/utils.js"); +Script.include("../libraries/utils.js"); var RIGHT_HAND = 1; From a264ed479547e12b9bfb08d7ba935df08eb1cc51 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:38:57 -0800 Subject: [PATCH 0823/1003] Remove old includes in bubblewand --- examples/toybox/bubblewand/createWand.js | 1 - examples/toybox/bubblewand/wand.js | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/toybox/bubblewand/createWand.js b/examples/toybox/bubblewand/createWand.js index d62c2064cf..4f4bc39e2c 100644 --- a/examples/toybox/bubblewand/createWand.js +++ b/examples/toybox/bubblewand/createWand.js @@ -10,7 +10,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ -Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; diff --git a/examples/toybox/bubblewand/wand.js b/examples/toybox/bubblewand/wand.js index c8ba51f51d..4bdc789612 100644 --- a/examples/toybox/bubblewand/wand.js +++ b/examples/toybox/bubblewand/wand.js @@ -14,7 +14,6 @@ (function () { - Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx"; From 5f084730d928605929e4d939c73f65bde2dacc14 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:39:12 -0800 Subject: [PATCH 0824/1003] Remove old includes in doll.js --- examples/toybox/doll/doll.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/toybox/doll/doll.js b/examples/toybox/doll/doll.js index 8f8a8cd840..577f86cae2 100644 --- a/examples/toybox/doll/doll.js +++ b/examples/toybox/doll/doll.js @@ -13,7 +13,6 @@ /*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ (function() { - Script.include("../../utilities.js"); Script.include("../../libraries/utils.js"); var _this; // this is the "constructor" for the entity as a JS object we don't do much here From 031a93cb0ea774f7aab30157a3c462c04e191a97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:39:31 -0800 Subject: [PATCH 0825/1003] Fix utils include in createPingPongGun.js --- examples/toybox/ping_pong_gun/createPingPongGun.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toybox/ping_pong_gun/createPingPongGun.js b/examples/toybox/ping_pong_gun/createPingPongGun.js index 9639f75320..705671e784 100644 --- a/examples/toybox/ping_pong_gun/createPingPongGun.js +++ b/examples/toybox/ping_pong_gun/createPingPongGun.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ -Script.include("../../utilities.js"); +Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath('pingPongGun.js'); From 08c27599472978cce6310e9ea4275efc94f1d203 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 10 Nov 2015 11:39:50 -0800 Subject: [PATCH 0826/1003] Remove unused include in createTargets.js --- examples/toybox/ping_pong_gun/createTargets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toybox/ping_pong_gun/createTargets.js b/examples/toybox/ping_pong_gun/createTargets.js index fb286b1928..fde0d6f54a 100644 --- a/examples/toybox/ping_pong_gun/createTargets.js +++ b/examples/toybox/ping_pong_gun/createTargets.js @@ -10,7 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ -Script.include("../../utilities.js"); + Script.include("../../libraries/utils.js"); var scriptURL = Script.resolvePath('wallTarget.js'); From d3d5d3a19e4970707e5c552585aa336c638beece Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 10 Nov 2015 12:02:20 -0800 Subject: [PATCH 0827/1003] Removing useless code --- interface/src/avatar/Avatar.cpp | 11 ----------- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/MyAvatar.cpp | 8 -------- interface/src/avatar/MyAvatar.h | 1 - libraries/avatars/src/AvatarData.cpp | 15 --------------- libraries/avatars/src/AvatarData.h | 1 - libraries/avatars/src/Recording.cpp | 2 +- 7 files changed, 1 insertion(+), 38 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index a5ae8cddda..b979334383 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -888,17 +888,6 @@ glm::quat Avatar::getJointRotation(int index) const { return rotation; } -QVector Avatar::getJointTranslations() const { - if (QThread::currentThread() != thread()) { - return AvatarData::getJointTranslations(); - } - QVector jointTranslations(_skeletonModel.getJointStateCount()); - for (int i = 0; i < _skeletonModel.getJointStateCount(); ++i) { - _skeletonModel.getJointTranslation(i, jointTranslations[i]); - } - return jointTranslations; -} - glm::vec3 Avatar::getJointTranslation(int index) const { if (QThread::currentThread() != thread()) { return AvatarData::getJointTranslation(index); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index cb7c533db6..44b5d91015 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -103,7 +103,6 @@ public: virtual QVector getJointRotations() const; virtual glm::quat getJointRotation(int index) const; - virtual QVector getJointTranslations() const; virtual glm::vec3 getJointTranslation(int index) const; virtual int getJointIndex(const QString& name) const; virtual QStringList getJointNames() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8595fa850e..6f60ad179c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1132,14 +1132,6 @@ void MyAvatar::setJointRotations(QVector jointRotations) { } } -void MyAvatar::setJointTranslations(QVector jointTranslations) { - int numStates = glm::min(_skeletonModel.getJointStateCount(), jointTranslations.size()); - for (int i = 0; i < numStates; ++i) { - // HACK: ATM only Recorder calls setJointTranslations() so we hardcode its priority here - _skeletonModel.setJointTranslation(i, true, jointTranslations[i], RECORDER_PRIORITY); - } -} - void MyAvatar::setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation), diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d6f51636f3..da836b7f15 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -192,7 +192,6 @@ public: void clearLookAtTargetAvatar(); virtual void setJointRotations(QVector jointRotations) override; - virtual void setJointTranslations(QVector jointTranslations) override; virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation) override; virtual void setJointRotation(int index, const glm::quat& rotation) override; virtual void setJointTranslation(int index, const glm::vec3& translation) override; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a13c01901e..a698c6f374 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1146,21 +1146,6 @@ void AvatarData::setJointRotations(QVector jointRotations) { } } -QVector AvatarData::getJointTranslations() const { - if (QThread::currentThread() != thread()) { - QVector result; - QMetaObject::invokeMethod(const_cast(this), - "getJointTranslations", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QVector, result)); - return result; - } - QVector jointTranslations(_jointData.size()); - for (int i = 0; i < _jointData.size(); ++i) { - jointTranslations[i] = _jointData[i].translation; - } - return jointTranslations; -} - void AvatarData::setJointTranslations(QVector jointTranslations) { if (QThread::currentThread() != thread()) { QVector result; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 766f31dda1..9079f15f53 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -261,7 +261,6 @@ public: Q_INVOKABLE virtual QVector getJointRotations() const; Q_INVOKABLE virtual void setJointRotations(QVector jointRotations); - Q_INVOKABLE virtual QVector getJointTranslations() const; Q_INVOKABLE virtual void setJointTranslations(QVector jointTranslations); Q_INVOKABLE virtual void clearJointsData(); diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index fa343630ed..26c5ab66dd 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -238,7 +238,7 @@ void writeRecordingToFile(RecordingPointer recording, const QString& filename) { } for (quint32 j = 0; j < numJoints; j++) { const auto& joint = jointArray[j]; - if (joint.rotationSet) { + if (true) { //(joint.rotationSet) { writeQuat(stream, joint.rotation); mask.setBit(maskIndex); } From bf0677a0cea84edcba4887ded70e4f3e1500b7d1 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 10 Nov 2015 13:42:15 -0800 Subject: [PATCH 0828/1003] Moved helicopter scripts into drylake folder --- examples/{helicopter => drylake}/explodeHelicopter.js | 0 examples/{helicopter => drylake}/helicopter.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/{helicopter => drylake}/explodeHelicopter.js (100%) rename examples/{helicopter => drylake}/helicopter.js (100%) diff --git a/examples/helicopter/explodeHelicopter.js b/examples/drylake/explodeHelicopter.js similarity index 100% rename from examples/helicopter/explodeHelicopter.js rename to examples/drylake/explodeHelicopter.js diff --git a/examples/helicopter/helicopter.js b/examples/drylake/helicopter.js similarity index 100% rename from examples/helicopter/helicopter.js rename to examples/drylake/helicopter.js From e7a2e82745f7763b0175f6053665b63f21d34736 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 10 Nov 2015 13:58:07 -0800 Subject: [PATCH 0829/1003] remove bball and targets resetter AC since we are doing this with hidden reset entities now --- .../AC_scripts/originalPositionResetter.js | 217 ------------------ 1 file changed, 217 deletions(-) delete mode 100644 examples/toybox/AC_scripts/originalPositionResetter.js diff --git a/examples/toybox/AC_scripts/originalPositionResetter.js b/examples/toybox/AC_scripts/originalPositionResetter.js deleted file mode 100644 index d3112732f7..0000000000 --- a/examples/toybox/AC_scripts/originalPositionResetter.js +++ /dev/null @@ -1,217 +0,0 @@ -// -// originalPositionResetter.js -// toybox -// -// Created by James B. Pollack @imgntn 10/16/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 -// -var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; - -var TARGET_MODEL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target.fbx"; -var TARGET_COLLISION_HULL_URL = HIFI_PUBLIC_BUCKET + "models/ping_pong_gun/target_collision_hull.obj"; -var TARGET_DIMENSIONS = { - x: 0.06, - y: 0.42, - z: 0.42 -}; -var TARGET_ROTATION = Quat.fromPitchYawRollDegrees(0, -55.25, 0); - -var targetsScriptURL = Script.resolvePath('../ping_pong_gun/wallTarget.js'); - - -var basketballURL = HIFI_PUBLIC_BUCKET + "models/content/basketball2.fbx"; - -var NUMBER_OF_BALLS = 4; -var BALL_DIAMETER = 0.30; -var RESET_DISTANCE = 1; -var MINIMUM_MOVE_LENGTH = 0.05; - -var totalTime = 0; -var lastUpdate = 0; -var UPDATE_INTERVAL = 1 / 5; // 5fps - -var Resetter = { - searchForEntitiesToResetToOriginalPosition: function(searchOrigin, objectName) { - var ids = Entities.findEntities(searchOrigin, 5); - var objects = []; - var i; - var entityID; - var name; - for (i = 0; i < ids.length; i++) { - entityID = ids[i]; - name = Entities.getEntityProperties(entityID, "name").name; - if (name === objectName) { - //we found an object to reset - objects.push(entityID); - } - } - return objects; - }, - deleteObjects: function(objects) { - while (objects.length > 0) { - Entities.deleteEntity(objects.pop()); - } - }, - createBasketBalls: function() { - var position = { - x: 542.86, - y: 494.84, - z: 475.06 - }; - var i; - var ballPosition; - var collidingBall; - for (i = 0; i < NUMBER_OF_BALLS; i++) { - ballPosition = { - x: position.x, - y: position.y + BALL_DIAMETER * 2, - z: position.z + (BALL_DIAMETER) - (BALL_DIAMETER * i) - }; - - collidingBall = Entities.addEntity({ - type: "Model", - name: 'Hifi-Basketball', - shapeType: 'Sphere', - position: ballPosition, - dimensions: { - x: BALL_DIAMETER, - y: BALL_DIAMETER, - z: BALL_DIAMETER - }, - restitution: 1.0, - linearDamping: 0.00001, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - collisionsWillMove: true, - ignoreForCollisions: false, - modelURL: basketballURL, - userData: JSON.stringify({ - originalPositionKey: { - originalPosition: ballPosition - }, - resetMe: { - resetMe: true - }, - grabbable: { - invertSolidWhileHeld: true - } - }) - }); - - } - }, - testBallDistanceFromStart: function(balls) { - var resetCount = 0; - balls.forEach(function(ball, index) { - var properties = Entities.getEntityProperties(ball, ["position", "userData"]); - var currentPosition = properties.position; - var originalPosition = properties.userData.originalPositionKey.originalPosition; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); - if (length > RESET_DISTANCE) { - Script.setTimeout(function() { - var newPosition = Entities.getEntityProperties(ball, "position").position; - var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); - if (moving < MINIMUM_MOVE_LENGTH) { - if (resetCount === balls.length) { - this.deleteObjects(balls); - this.createBasketBalls(); - } - } - }, 200); - } - }); - }, - testTargetDistanceFromStart: function(targets) { - targets.forEach(function(target, index) { - var properties = Entities.getEntityProperties(target, ["position", "userData"]); - var currentPosition = properties.position; - var originalPosition = properties.userData.originalPositionKey.originalPosition; - var distance = Vec3.subtract(originalPosition, currentPosition); - var length = Vec3.length(distance); - if (length > RESET_DISTANCE) { - Script.setTimeout(function() { - var newPosition = Entities.getEntityProperties(target, "position").position; - var moving = Vec3.length(Vec3.subtract(currentPosition, newPosition)); - if (moving < MINIMUM_MOVE_LENGTH) { - - Entities.deleteEntity(target); - - var targetProperties = { - name: 'Hifi-Target', - type: 'Model', - modelURL: TARGET_MODEL_URL, - shapeType: 'compound', - collisionsWillMove: true, - dimensions: TARGET_DIMENSIONS, - compoundShapeURL: TARGET_COLLISION_HULL_URL, - position: originalPosition, - rotation: TARGET_ROTATION, - script: targetsScriptURL, - userData: JSON.stringify({ - resetMe: { - resetMe: true - }, - grabbableKey: { - grabbable: false - }, - originalPositionKey: originalPosition - - }) - }; - - Entities.addEntity(targetProperties); - } - }, 200); - } - - }); - - } -}; - -function update(deltaTime) { - - if (!Entities.serversExist() || !Entities.canRez()) { - return; - } - - - totalTime += deltaTime; - - // We don't want to edit the entity EVERY update cycle, because that's just a lot - // of wasted bandwidth and extra effort on the server for very little visual gain - if (totalTime - lastUpdate > UPDATE_INTERVAL) { - //do stuff - var balls = Resetter.searchForEntitiesToResetToOriginalPosition({ - x: 542.86, - y: 494.84, - z: 475.06 - }, "Hifi-Basketball"); - - var targets = Resetter.searchForEntitiesToResetToOriginalPosition({ - x: 548.68, - y: 497.30, - z: 509.74 - }, "Hifi-Target"); - - if (balls.length !== 0) { - Resetter.testBallDistanceFromStart(balls); - } - - if (targets.length !== 0) { - Resetter.testTargetDistanceFromStart(targets); - } - - lastUpdate = totalTime; - } - -} - -Script.update.connect(update); \ No newline at end of file From 5e4f30b2bb8d073238093cdaca2aaff23b7398ef Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 13:58:09 -0800 Subject: [PATCH 0830/1003] improve terse entity logging. change how action data sending is triggered --- .../src/octree/OctreeSendThread.cpp | 7 +- libraries/entities/src/EntityItem.cpp | 1 + .../entities/src/EntityScriptingInterface.cpp | 15 ++-- libraries/entities/src/EntityTree.cpp | 78 ++++++++++++++++++- 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 2696c92253..4e01d1562b 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -17,6 +17,7 @@ #include "OctreeSendThread.h" #include "OctreeServer.h" #include "OctreeServerConsts.h" +#include "OctreeLogging.h" quint64 startSceneSleepTime = 0; quint64 endSceneSleepTime = 0; @@ -571,9 +572,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus if (somethingToSend) { - qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval - << " maxPacketsPerInterval = " << maxPacketsPerInterval - << " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval; + qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval + << " maxPacketsPerInterval = " << maxPacketsPerInterval + << " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index cce3045049..dbea43d514 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1770,6 +1770,7 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { serializedActionsStream << serializedActions; if (result.size() >= _maxActionsDataSize) { + qDebug() << "EntityItem::serializeActions size is too large -- " << result.size() << ">=" << _maxActionsDataSize; success = false; return; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8ca0e9b5fa..e5caab3a71 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -611,7 +611,8 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, const QVariantMap& arguments) { QUuid actionID = QUuid::createUuid(); auto actionFactory = DependencyManager::get(); - bool success = actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + bool success = true; + actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { // create this action even if the entity doesn't have physics info. it will often be the // case that a script adds an action immediately after an object is created, and the physicsInfo // is computed asynchronously. @@ -620,17 +621,18 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, // } EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); if (actionType == ACTION_TYPE_NONE) { + success = false; return false; } EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments); if (action) { - entity->addAction(simulation, action); + success = entity->addAction(simulation, action); auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); if (entity->getSimulatorID() != myNodeID) { entity->flagForOwnership(); } - return true; + return false; // Physics will cause a packet to be sent, so don't send from here. } return false; }); @@ -656,9 +658,12 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& } bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) { - return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { - return entity->removeAction(simulation, actionID); + bool success = false; + actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { + success = entity->removeAction(simulation, actionID); + return false; // Physics will cause a packet to be sent, so don't send from here. }); + return success; } QVector EntityScriptingInterface::getActionIDs(const QUuid& entityID) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8e32158362..f1a85a1b6d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -612,6 +612,14 @@ EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entit } void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties) { + static quint64 lastTerseLog = 0; + quint64 now = usecTimestampNow(); + + if (now - lastTerseLog > USECS_PER_SECOND) { + qCDebug(entities) << "-------------------------"; + } + lastTerseLog = now; + if (properties.simulationOwnerChanged()) { int simIndex = changedProperties.indexOf("simulationOwner"); if (simIndex >= 0) { @@ -619,6 +627,70 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { + glm::vec3 value = properties.getVelocity(); + QString changeHint = "0"; + if (value.x + value.y + value.z > 0) { + changeHint = "+"; + } else if (value.x + value.y + value.z < 0) { + changeHint = "-"; + } + changedProperties[index] = QString("velocity:") + changeHint; + } + } + + if (properties.gravityChanged()) { + int index = changedProperties.indexOf("gravity"); + if (index >= 0) { + glm::vec3 value = properties.getGravity(); + QString changeHint = "0"; + if (value.x + value.y + value.z > 0) { + changeHint = "+"; + } else if (value.x + value.y + value.z < 0) { + changeHint = "-"; + } + changedProperties[index] = QString("gravity:") + changeHint; + } + } + + if (properties.actionDataChanged()) { + int index = changedProperties.indexOf("actionData"); + if (index >= 0) { + QByteArray value = properties.getActionData(); + QString changeHint = "0"; + if (value.size() > 0) { + changeHint = "+"; + } + changedProperties[index] = QString("actionData:") + changeHint; + } + } + + if (properties.ignoreForCollisionsChanged()) { + int index = changedProperties.indexOf("ignoreForCollisions"); + if (index >= 0) { + bool value = properties.getIgnoreForCollisions(); + QString changeHint = "0"; + if (value) { + changeHint = "1"; + } + changedProperties[index] = QString("ignoreForCollisions:") + changeHint; + } + } + + if (properties.collisionsWillMoveChanged()) { + int index = changedProperties.indexOf("collisionsWillMove"); + if (index >= 0) { + bool value = properties.getCollisionsWillMove(); + QString changeHint = "0"; + if (value) { + changeHint = "1"; + } + changedProperties[index] = QString("collisionsWillMove:") + changeHint; + } + } } int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, @@ -673,7 +745,9 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi if (wantTerseEditLogging()) { QList changedProperties = properties.listChangedProperties(); fixupTerseEditLogging(properties, changedProperties); - qCDebug(entities) << "edit" << entityItemID.toString() << changedProperties; + QString itemName = + existingEntity->getName() != "" ? existingEntity->getName() : entityItemID.toString(); + qCDebug(entities) << "edit" << itemName << changedProperties; } endLogging = usecTimestampNow(); @@ -703,7 +777,7 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi if (wantTerseEditLogging()) { QList changedProperties = properties.listChangedProperties(); fixupTerseEditLogging(properties, changedProperties); - qCDebug(entities) << "add" << entityItemID.toString() << changedProperties; + qCDebug(entities) << "add" << entityItemID << changedProperties; } endLogging = usecTimestampNow(); From 82bd5060860a2cb4ab25f7c8b10bb7422434b08f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 14:32:57 -0800 Subject: [PATCH 0831/1003] adjust how EntityMotionState updates its idea of what the server knows about an entity --- .../entities/src/EntityScriptingInterface.cpp | 21 +++++++++---------- libraries/physics/src/EntityMotionState.cpp | 5 +++++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index e5caab3a71..bc57f2c72c 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -611,7 +611,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, const QVariantMap& arguments) { QUuid actionID = QUuid::createUuid(); auto actionFactory = DependencyManager::get(); - bool success = true; + bool success = false; actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) { // create this action even if the entity doesn't have physics info. it will often be the // case that a script adds an action immediately after an object is created, and the physicsInfo @@ -621,20 +621,19 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, // } EntityActionType actionType = EntityActionInterface::actionTypeFromString(actionTypeString); if (actionType == ACTION_TYPE_NONE) { - success = false; return false; } EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments); - if (action) { - success = entity->addAction(simulation, action); - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); - if (entity->getSimulatorID() != myNodeID) { - entity->flagForOwnership(); - } - return false; // Physics will cause a packet to be sent, so don't send from here. + if (!action) { + return false; } - return false; + success = entity->addAction(simulation, action); + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + if (entity->getSimulatorID() != myNodeID) { + entity->flagForOwnership(); + } + return false; // Physics will cause a packet to be sent, so don't send from here. }); if (success) { return actionID; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c8a0f87b6d..2a7bde2377 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -86,6 +86,11 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { return; } + if (_entity->shouldSuppressLocationEdits()) { + // if we send now, the changes will be ignored, so don't update our idea of what the server knows. + return; + } + _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); From e067195dfe3585c99c7ed299d111c08ba637a6bb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 14:42:40 -0800 Subject: [PATCH 0832/1003] adjust how EntityMotionState updates its idea of what the server knows about an entity --- libraries/physics/src/EntityMotionState.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 2a7bde2377..e61523587e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -86,6 +86,8 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { return; } + _serverActionData = _entity->getActionData(); + if (_entity->shouldSuppressLocationEdits()) { // if we send now, the changes will be ignored, so don't update our idea of what the server knows. return; @@ -96,7 +98,6 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { _serverVelocity = _entity->getVelocity(); _serverAngularVelocity = _entity->getAngularVelocity(); _serverAcceleration = _entity->getAcceleration(); - _serverActionData = _entity->getActionData(); } // virtual @@ -446,11 +447,13 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q } // remember properties for local server prediction - _serverPosition = _entity->getPosition(); - _serverRotation = _entity->getRotation(); - _serverVelocity = _entity->getVelocity(); - _serverAcceleration = _entity->getAcceleration(); - _serverAngularVelocity = _entity->getAngularVelocity(); + if (!_entity->shouldSuppressLocationEdits()) { + _serverPosition = _entity->getPosition(); + _serverRotation = _entity->getRotation(); + _serverVelocity = _entity->getVelocity(); + _serverAcceleration = _entity->getAcceleration(); + _serverAngularVelocity = _entity->getAngularVelocity(); + } _serverActionData = _entity->getActionData(); EntityItemProperties properties; From df5d161721b313bb9e359f0200e777ac1b60641a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 15:02:17 -0800 Subject: [PATCH 0833/1003] adjust how EntityMotionState updates its idea of what the server knows about an entity --- libraries/entities/src/EntityTree.cpp | 12 ++++++++++++ libraries/physics/src/EntityMotionState.cpp | 21 ++++++++++----------- libraries/physics/src/EntityMotionState.h | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f1a85a1b6d..9785e12736 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -691,6 +691,18 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { + bool value = properties.getLocked(); + QString changeHint = "0"; + if (value) { + changeHint = "1"; + } + changedProperties[index] = QString("locked:") + changeHint; + } + } } int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e61523587e..c7b72968c9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -87,17 +87,15 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { } _serverActionData = _entity->getActionData(); - - if (_entity->shouldSuppressLocationEdits()) { - // if we send now, the changes will be ignored, so don't update our idea of what the server knows. - return; + if (!_serverShouldSuppressLocationEdits) { + _serverPosition = _entity->getPosition(); + _serverRotation = _entity->getRotation(); + _serverVelocity = _entity->getVelocity(); + _serverAngularVelocity = _entity->getAngularVelocity(); + _serverAcceleration = _entity->getAcceleration(); } - _serverPosition = _entity->getPosition(); - _serverRotation = _entity->getRotation(); - _serverVelocity = _entity->getVelocity(); - _serverAngularVelocity = _entity->getAngularVelocity(); - _serverAcceleration = _entity->getAcceleration(); + _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); } // virtual @@ -264,6 +262,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); + _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); _sentInactive = true; return false; } @@ -447,14 +446,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q } // remember properties for local server prediction - if (!_entity->shouldSuppressLocationEdits()) { + if (!_serverShouldSuppressLocationEdits) { _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); _serverAcceleration = _entity->getAcceleration(); _serverAngularVelocity = _entity->getAngularVelocity(); } - _serverActionData = _entity->getActionData(); + _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); EntityItemProperties properties; diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 188e7096b9..f70eff44a4 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -110,6 +110,7 @@ protected: glm::vec3 _serverGravity; glm::vec3 _serverAcceleration; QByteArray _serverActionData; + bool _serverShouldSuppressLocationEdits = false; uint32_t _lastMeasureStep; glm::vec3 _lastVelocity; From 97fec5b814a357d07dc8f065793e6697fe4b26f5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 15:09:53 -0800 Subject: [PATCH 0834/1003] adjust how EntityMotionState updates its idea of what the server knows about an entity --- libraries/physics/src/EntityMotionState.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c7b72968c9..7c054598ab 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -445,24 +445,25 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _sentInactive = false; } - // remember properties for local server prediction + EntityItemProperties properties; + + + // explicitly set the properties that changed so that they will be packed if (!_serverShouldSuppressLocationEdits) { + // remember properties for local server prediction _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); _serverAcceleration = _entity->getAcceleration(); _serverAngularVelocity = _entity->getAngularVelocity(); + + properties.setPosition(_serverPosition); + properties.setRotation(_serverRotation); + properties.setVelocity(_serverVelocity); + properties.setAcceleration(_serverAcceleration); + properties.setAngularVelocity(_serverAngularVelocity); } _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); - - EntityItemProperties properties; - - // explicitly set the properties that changed so that they will be packed - properties.setPosition(_serverPosition); - properties.setRotation(_serverRotation); - properties.setVelocity(_serverVelocity); - properties.setAcceleration(_serverAcceleration); - properties.setAngularVelocity(_serverAngularVelocity); properties.setActionData(_serverActionData); // set the LastEdited of the properties but NOT the entity itself From 774e2ab6d6338f6c6d95426ae6c9c0c3f3616288 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 15:13:07 -0800 Subject: [PATCH 0835/1003] adjust how EntityMotionState updates its idea of what the server knows about an entity --- libraries/physics/src/EntityMotionState.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 7c054598ab..9be6dd38b1 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -464,6 +464,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q properties.setAngularVelocity(_serverAngularVelocity); } _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); + _serverActionData = _entity->getActionData(); properties.setActionData(_serverActionData); // set the LastEdited of the properties but NOT the entity itself From 006a1d60c82699612fa2d98629ba990735a3e356 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 10 Nov 2015 15:31:51 -0800 Subject: [PATCH 0836/1003] keep some additional erase entities history and send to viewers --- .../src/entities/EntityNodeData.h | 6 +- .../src/entities/EntityServer.cpp | 5 +- libraries/entities/src/EntityTree.cpp | 116 +++++++++--------- 3 files changed, 65 insertions(+), 62 deletions(-) diff --git a/assignment-client/src/entities/EntityNodeData.h b/assignment-client/src/entities/EntityNodeData.h index e4008fcb03..69da9ee14b 100644 --- a/assignment-client/src/entities/EntityNodeData.h +++ b/assignment-client/src/entities/EntityNodeData.h @@ -18,9 +18,7 @@ class EntityNodeData : public OctreeQueryNode { public: - EntityNodeData() : - OctreeQueryNode(), - _lastDeletedEntitiesSentAt(0) { } + EntityNodeData() : OctreeQueryNode() { } virtual PacketType getMyPacketType() const { return PacketType::EntityData; } @@ -28,7 +26,7 @@ public: void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; } private: - quint64 _lastDeletedEntitiesSentAt; + quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() }; }; #endif // hifi_EntityNodeData_h diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index f2a4c2664a..e2941541af 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -82,7 +82,6 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) { EntityNodeData* nodeData = static_cast(node->getLinkedData()); if (nodeData) { quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt(); - EntityTreePointer tree = std::static_pointer_cast(_tree); shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt); } @@ -97,7 +96,6 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN if (nodeData) { quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt(); quint64 deletePacketSentAt = usecTimestampNow(); - EntityTreePointer tree = std::static_pointer_cast(_tree); bool hasMoreToSend = true; @@ -127,7 +125,6 @@ void EntityServer::pruneDeletedEntities() { if (tree->hasAnyDeletedEntities()) { quint64 earliestLastDeletedEntitiesSent = usecTimestampNow() + 1; // in the future - DependencyManager::get()->eachNode([&earliestLastDeletedEntitiesSent](const SharedNodePointer& node) { if (node->getLinkedData()) { EntityNodeData* nodeData = static_cast(node->getLinkedData()); @@ -138,6 +135,8 @@ void EntityServer::pruneDeletedEntities() { } }); + int EXTRA_SECONDS_TO_KEEP = 4; + earliestLastDeletedEntitiesSent -= USECS_PER_SECOND * EXTRA_SECONDS_TO_KEEP; tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8e32158362..c69c2daa16 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -384,16 +384,17 @@ void EntityTree::deleteEntities(QSet entityIDs, bool force, bool i } void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) { + quint64 deletedAt = usecTimestampNow(); const RemovedEntities& entities = theOperator.getEntities(); foreach(const EntityToDeleteDetails& details, entities) { EntityItemPointer theEntity = details.entity; if (getIsServer()) { // set up the deleted entities ID - quint64 deletedAt = usecTimestampNow(); - _recentlyDeletedEntitiesLock.lockForWrite(); - _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); - _recentlyDeletedEntitiesLock.unlock(); + { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); + } } if (_simulation) { @@ -802,18 +803,23 @@ void EntityTree::update() { } bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { + int EXTRA_SECONDS_TO_CONSIDER = 4; + quint64 considerEntitiesSince = sinceTime - (USECS_PER_SECOND * EXTRA_SECONDS_TO_CONSIDER); + // we can probably leverage the ordered nature of QMultiMap to do this quickly... bool hasSomethingNewer = false; + { + QReadLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntitiesLock.lockForRead(); - QMultiMap::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin(); - while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) { - if (iterator.key() > sinceTime) { - hasSomethingNewer = true; + QMultiMap::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin(); + while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) { + if (iterator.key() > considerEntitiesSince) { + hasSomethingNewer = true; + } + ++iterator; } - ++iterator; } - _recentlyDeletedEntitiesLock.unlock(); + return hasSomethingNewer; } @@ -821,6 +827,8 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, bool& hasMore) { + int EXTRA_SECONDS_TO_CONSIDER = 4; + quint64 considerEntitiesSince = sinceTime - (USECS_PER_SECOND * EXTRA_SECONDS_TO_CONSIDER); auto deletesPacket = NLPacket::create(PacketType::EntityErase); // pack in flags @@ -841,48 +849,46 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been // deleted since we last sent to this node - _recentlyDeletedEntitiesLock.lockForRead(); + { + QReadLocker locker(&_recentlyDeletedEntitiesLock); - bool hasFilledPacket = false; + bool hasFilledPacket = false; - auto it = _recentlyDeletedEntityItemIDs.constBegin(); - while (it != _recentlyDeletedEntityItemIDs.constEnd()) { - QList values = _recentlyDeletedEntityItemIDs.values(it.key()); - for (int valueItem = 0; valueItem < values.size(); ++valueItem) { + auto it = _recentlyDeletedEntityItemIDs.constBegin(); + while (it != _recentlyDeletedEntityItemIDs.constEnd()) { + QList values = _recentlyDeletedEntityItemIDs.values(it.key()); + for (int valueItem = 0; valueItem < values.size(); ++valueItem) { - // if the timestamp is more recent then out last sent time, include it - if (it.key() > sinceTime) { - QUuid entityID = values.at(valueItem); - deletesPacket->write(entityID.toRfc4122()); + // if the timestamp is more recent then out last sent time, include it + if (it.key() > considerEntitiesSince) { + QUuid entityID = values.at(valueItem); + deletesPacket->write(entityID.toRfc4122()); + ++numberOfIDs; - ++numberOfIDs; - - // check to make sure we have room for one more ID - if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) { - hasFilledPacket = true; - break; + // check to make sure we have room for one more ID + if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) { + hasFilledPacket = true; + break; + } } } + + // check to see if we're about to return + if (hasFilledPacket) { + // let our caller know how far we got + sinceTime = it.key(); + break; + } + + ++it; } - // check to see if we're about to return - if (hasFilledPacket) { - // let our caller know how far we got - sinceTime = it.key(); - - break; + // if we got to the end, then we're done sending + if (it == _recentlyDeletedEntityItemIDs.constEnd()) { + hasMore = false; } - - ++it; } - // if we got to the end, then we're done sending - if (it == _recentlyDeletedEntityItemIDs.constEnd()) { - hasMore = false; - } - - _recentlyDeletedEntitiesLock.unlock(); - // replace the count for the number of included IDs deletesPacket->seek(numberOfIDsPos); deletesPacket->writePrimitive(numberOfIDs); @@ -895,23 +901,23 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { QSet keysToRemove; - _recentlyDeletedEntitiesLock.lockForWrite(); - QMultiMap::iterator iterator = _recentlyDeletedEntityItemIDs.begin(); + { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + QMultiMap::iterator iterator = _recentlyDeletedEntityItemIDs.begin(); - // First find all the keys in the map that are older and need to be deleted - while (iterator != _recentlyDeletedEntityItemIDs.end()) { - if (iterator.key() <= sinceTime) { - keysToRemove << iterator.key(); + // First find all the keys in the map that are older and need to be deleted + while (iterator != _recentlyDeletedEntityItemIDs.end()) { + if (iterator.key() <= sinceTime) { + keysToRemove << iterator.key(); + } + ++iterator; } - ++iterator; - } - // Now run through the keysToRemove and remove them - foreach (quint64 value, keysToRemove) { - _recentlyDeletedEntityItemIDs.remove(value); + // Now run through the keysToRemove and remove them + foreach (quint64 value, keysToRemove) { + _recentlyDeletedEntityItemIDs.remove(value); + } } - - _recentlyDeletedEntitiesLock.unlock(); } From 3316b63bf6e8efb1c9a494dd5ce027983b76c026 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 10 Nov 2015 15:46:53 -0800 Subject: [PATCH 0837/1003] add a fixme comment --- libraries/entities/src/EntityTree.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c69c2daa16..34b09eb67c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -862,6 +862,12 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // if the timestamp is more recent then out last sent time, include it if (it.key() > considerEntitiesSince) { QUuid entityID = values.at(valueItem); + + // FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server + // to the client. These were causing "lost" entities like flashlights and laser pointers + // now that we keep around some additional history of the erased entities and resend that + // history for a longer time window, these entities are not "lost". But we haven't yet + // found/fixed the underlying issue that caused bad UUIDs to be sent to some users. deletesPacket->write(entityID.toRfc4122()); ++numberOfIDs; From fc3602d780ff9e3b4e86fe532710dd31a9924939 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 10 Nov 2015 16:46:58 -0800 Subject: [PATCH 0838/1003] change context menu to RightPrimaryThumb, add filter to mouse click to not count slow clicks --- examples/libraries/omniTool.js | 2 +- interface/resources/controllers/standard.json | 2 +- .../src/input-plugins/KeyboardMouseDevice.cpp | 11 +++++++---- .../src/input-plugins/KeyboardMouseDevice.h | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index 4c995d6528..e0e5da4496 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -64,7 +64,7 @@ OmniTool = function(left) { }); this.mapping = Controller.newMapping(); - this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb).to(function(value){ + this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightSecondaryThumb).to(function(value){ that.onUpdateTrigger(value); }) this.mapping.enable(); diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index d4988fc00d..4833388080 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -33,7 +33,7 @@ { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, { "from": "Standard.LeftSecondaryThumb", "to": "Actions.CycleCamera" }, - { "from": "Standard.RightSecondaryThumb", "to": "Actions.ContextMenu" }, + { "from": "Standard.RightPrimaryThumb", "to": "Actions.ContextMenu" }, { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index e91ea90aaf..6a08a50b13 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -16,6 +16,7 @@ #include #include +#include const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; @@ -64,6 +65,7 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic } _lastCursor = event->pos(); _mousePressAt = event->pos(); + _mousePressTime = usecTimestampNow(); eraseMouseClicked(); } @@ -72,10 +74,11 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int dev auto input = _inputDevice->makeInput((Qt::MouseButton) event->button()); _inputDevice->_buttonPressedMap.erase(input.getChannel()); - // if we pressed and released at the same location, then create a "_CLICKED" input for this button - // we might want to add some small tolerance to this so if you do a small drag it still counts as - // a clicked. - if (_mousePressAt == event->pos()) { + // if we pressed and released at the same location within a small time window, then create a "_CLICKED" + // input for this button we might want to add some small tolerance to this so if you do a small drag it + // till counts as a clicked. + static const int CLICK_TIME = USECS_PER_MSEC * 500; // 500 ms to click + if (_mousePressAt == event->pos() && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) { _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 4abdc44478..7182424df0 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -116,6 +116,7 @@ public: protected: QPoint _lastCursor; QPoint _mousePressAt; + quint64 _mousePressTime; glm::vec2 _lastTouch; std::shared_ptr _inputDevice { std::make_shared() }; From e4b271d6d8ffe6f3e554d65f641560d7aed4364a Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 10 Nov 2015 16:49:46 -0800 Subject: [PATCH 0839/1003] dynamic loading content from s3 --- examples/marketplace/dynamicLoader.js | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 examples/marketplace/dynamicLoader.js diff --git a/examples/marketplace/dynamicLoader.js b/examples/marketplace/dynamicLoader.js new file mode 100644 index 0000000000..5c9c4560d8 --- /dev/null +++ b/examples/marketplace/dynamicLoader.js @@ -0,0 +1,74 @@ +var BASE_URL = "https://hifi-public.s3.amazonaws.com/"; +var models = []; +var floorPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));; +floorPosition.y = MyAvatar.position.y - 5; +var floor = Entities.addEntity({ + type: "Model", + modelURL: "https://hifi-public.s3.amazonaws.com/ozan/3d_marketplace/props/floor/3d_mp_floor.fbx", + position: floorPosition, + shapeType: 'box', + dimensions: { + x: 1000, + y: 9, + z: 1000 + } +}); + +var urls = []; +var req = new XMLHttpRequest(); +req.open("GET", "https://serene-headland-4300.herokuapp.com/?assetDir=ozan/3d_marketplace/sets", false); +// req.open("GET", "http://localhost:5000", false); +req.send(); + +var res = req.responseText; +var urls = JSON.parse(res).urls; +if (urls.length > 0) { + print("WAAAH") + // We've got an array of urls back from server- let's display them in grid + createGrid(); +} + +function createGrid() { + var fbxUrls = urls.filter(function(url) { + return url.indexOf('fbx') !== -1; + }); + print(JSON.stringify(fbxUrls)); + + var modelParams = { + type: "Model", + // dimensions: { + // x: 31.85, + // y: 7.75, + // z: 54.51 + // }, + }; + + var modelPosition = { + x: floorPosition.x + 10, + y: floorPosition.y + 8.5, + z: floorPosition.z + }; + + for (var i = 0; i < fbxUrls.length; i++) { + if(i % 2 === 0) { + modelPosition.x = floorPosition.x - 40 + } else { + modelPosition.x = floorPosition.x + 40 + } + modelPosition.z -= 30; + modelParams.position = modelPosition; + modelParams.modelURL = BASE_URL + fbxUrls[i] + var model = Entities.addEntity(modelParams); + models.push(model); + } +} + +function cleanup() { + Entities.deleteEntity(floor); + models.forEach(function(model) { + Entities.deleteEntity(model); + }); + Entities.deleteEntity(model); +} + +Script.scriptEnding.connect(cleanup); \ No newline at end of file From 0aefb8747f3c59c14138077ba25368e1b6fd6624 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 10 Nov 2015 16:55:17 -0800 Subject: [PATCH 0840/1003] Dynamic loading of models from specified directory --- examples/marketplace/dynamicLoader.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/marketplace/dynamicLoader.js b/examples/marketplace/dynamicLoader.js index 5c9c4560d8..b40eaa9dba 100644 --- a/examples/marketplace/dynamicLoader.js +++ b/examples/marketplace/dynamicLoader.js @@ -17,13 +17,11 @@ var floor = Entities.addEntity({ var urls = []; var req = new XMLHttpRequest(); req.open("GET", "https://serene-headland-4300.herokuapp.com/?assetDir=ozan/3d_marketplace/sets", false); -// req.open("GET", "http://localhost:5000", false); req.send(); var res = req.responseText; var urls = JSON.parse(res).urls; if (urls.length > 0) { - print("WAAAH") // We've got an array of urls back from server- let's display them in grid createGrid(); } @@ -36,11 +34,11 @@ function createGrid() { var modelParams = { type: "Model", - // dimensions: { - // x: 31.85, - // y: 7.75, - // z: 54.51 - // }, + dimensions: { + x: 10, + y: 10, + z: 10 + }, }; var modelPosition = { @@ -61,6 +59,15 @@ function createGrid() { var model = Entities.addEntity(modelParams); models.push(model); } + + Script.setTimeout(function() { + //Until we add callbacks on model loaded, we need to set a timeout and hope model is loaded by the time + //we hit it in order to set model dimensions correctly + for(var i = 0; i < models.length; i++){ + var modelDimensions = Entities.getEntityProperties(models[i], 'naturalDimensions').naturalDimensions; + Entities.editEntity(models[i], {dimensions: modelDimensions}); + } + }, 10000); } function cleanup() { From 66387c45307db73c8ef8aee357e72fb95a8a0588 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 17:02:57 -0800 Subject: [PATCH 0841/1003] use icons to show network and physics status --- .../src/RenderableEntityItem.h | 11 +++++++++++ .../src/RenderableModelEntityItem.cpp | 13 +++++++++---- libraries/gpu/src/gpu/Resource.h | 2 +- .../render-utils/src/RenderDeferredTask.cpp | 9 ++++----- libraries/render/src/render/Scene.h | 2 +- libraries/render/src/render/drawItemStatus.slf | 12 ++++++++---- libraries/render/src/render/drawItemStatus.slv | 17 +++++++++++------ 7 files changed, 45 insertions(+), 21 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 1832ef28c3..449ac40e5d 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -15,6 +15,17 @@ #include #include +// These or the icon "name" used by the render item status value, they correspond to the atlas texture used by the DrawItemStatus +// job in the current rendering pipeline defined as of now (11/2015) in render-utils/RenderDeferredTask.cpp. +enum class RenderItemStatusIcon { + PACKET_RECEIVED = 2, + PACKET_SENT = 1, + ACTIVE_IN_BULLET = 0, + SIMULATION_OWNER = 3, + + NONE = 255 +}; + class RenderableEntityItemProxy { public: diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index d33e52fa6b..83d18c0403 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -23,6 +23,7 @@ #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" #include "RenderableModelEntityItem.h" +#include "RenderableEntityItem.h" EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return std::make_shared(entityID, properties); @@ -189,7 +190,8 @@ void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item // Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green) return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : - render::Item::Status::Value::RED)); + render::Item::Status::Value::RED), + (unsigned char) RenderItemStatusIcon::PACKET_RECEIVED); }); statusGetters.push_back([entity] () -> render::Item::Status::Value { @@ -200,15 +202,18 @@ void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item // Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green) return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : - render::Item::Status::Value::CYAN)); + render::Item::Status::Value::CYAN), + (unsigned char)RenderItemStatusIcon::PACKET_SENT); }); statusGetters.push_back([entity] () -> render::Item::Status::Value { ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); if (motionState && motionState->isActive()) { - return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE); + return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); } - return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE); + return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); }); } diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index f330b0fd07..8d53d6e2e7 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -331,7 +331,7 @@ public: template Iterator begin() { return Iterator(&edit(0), _stride); } template Iterator end() { return Iterator(&edit(getNum()), _stride); } - template Iterator cbegin() const { return Iterator(&get(0), _stride); } + template Iterator cbegin() const { return Iterator(&get(), _stride); } template Iterator cend() const { return Iterator(&get(getNum()), _stride); } // the number of elements of the specified type fitting in the view size diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index baa4b8f259..e65018ad3d 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -112,12 +112,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); - // Grab a texture map representing the different status icons and assign that to the drawStatsuJob - auto iconMapPath = PathUtils::resourcesPath() + "images/hifi-logo.svg"; - - auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); - _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); + // Grab a texture map representing the different status icons and assign that to the drawStatsuJob + auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; + auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); + _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); _jobs.back().setEnabled(false); _drawStatusJobIndex = _jobs.size() - 1; diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 459f15055f..7176e5753e 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -212,7 +212,7 @@ public: const static Value INVALID; // Invalid value meanss the status won't show Value() {} - Value(float scale, float hue, unsigned char icon = 0) { setScale(scale); setColor(hue); setIcon(icon); } + Value(float scale, float hue, unsigned char icon = 0xFF) { setScale(scale); setColor(hue); setIcon(icon); } // It can be scaled in the range [0, 1] void setScale(float scale); diff --git a/libraries/render/src/render/drawItemStatus.slf b/libraries/render/src/render/drawItemStatus.slf index 3b902e151d..40cf450363 100644 --- a/libraries/render/src/render/drawItemStatus.slf +++ b/libraries/render/src/render/drawItemStatus.slf @@ -16,12 +16,16 @@ in vec3 varTexcoord; out vec4 outFragColor; uniform sampler2D _icons; - +vec2 getIconTexcoord(float icon, vec2 uv) { + const vec2 ICON_COORD_SIZE = vec2(0.0625, 1.0); + return vec2((uv.x + icon) * ICON_COORD_SIZE.x, uv.y * ICON_COORD_SIZE.y); +} void main(void) { - if (varTexcoord.z > 0.0f) { - outFragColor = texture(_icons, varTexcoord.xy) * varColor; + if (varTexcoord.z < 254.5) { + outFragColor = texture(_icons, getIconTexcoord(varTexcoord.z, varTexcoord.xy)) * varColor; } else { - outFragColor = varColor; + vec2 centerDir = varTexcoord.xy * 2.0f - 1.0f; + outFragColor = vec4(varColor.xyz, 1.0 - step(1.0f, dot(centerDir.xy, centerDir.xy))); } } diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index 8a9ca591c7..8c4f0724d5 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -47,11 +47,11 @@ vec3 paintRainbow(float normalizedHue) { vec3 unpackStatus(int v) { return vec3(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), clamp(float(uint((v >> 16) & 0xFF)) / 255.0, 0.0, 1.0), - clamp(float(int((v >> 24) & 0xFF)), 0.0, 256.0)); + clamp(float(int((v >> 24) & 0xFF)), 0.0, 255.0)); } void main(void) { - const vec2 ICON_PIXEL_SIZE = vec2(10, 10); + const vec2 ICON_PIXEL_SIZE = vec2(20, 20); const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); const int NUM_VERTICES = 6; const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES]( @@ -79,12 +79,19 @@ void main(void) { return; } + // Which quad vertex pos? + int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; + vec4 quadPos = UNIT_QUAD[twoTriID]; + // unpack to get x and y satus vec3 iconStatus = unpackStatus(inStatus[iconNum]); // Use the status for showing a color varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); + // Pass the texcoord and the z texcoord is representing the texture icon + varTexcoord = vec3((quadPos.xy + 1.0) * 0.5, iconStatus.z); + // Also changes the size of the notification vec2 iconScale = ICON_PIXEL_SIZE; iconScale = max(vec2(0, 0), (iconScale * iconStatus.x)); @@ -93,13 +100,11 @@ void main(void) { vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); // Final position in pixel space - int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; - vec4 pos = UNIT_QUAD[twoTriID]; - vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale; + vec2 quadPixelPos = offset.xy + quadPos.xy * 0.5 * iconScale; vec4 viewport; <$transformCameraViewport(cam, viewport)$>; vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w); gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0)); -} \ No newline at end of file +} From 39b92d2b5297d9d3edf0225a720ad6b5a8e0e526 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 17:06:12 -0800 Subject: [PATCH 0842/1003] terse log when ownership changes --- .../entities-renderer/src/RenderableEntityItem.cpp | 4 +--- libraries/entities-renderer/src/RenderableEntityItem.h | 1 + .../src/RenderableModelEntityItem.cpp | 4 ++-- .../src/RenderableParticleEffectEntityItem.cpp | 3 +++ .../src/RenderablePolyVoxEntityItem.cpp | 4 ++++ .../entities-renderer/src/RenderableZoneEntityItem.cpp | 10 +++++++++- libraries/entities/src/EntityTree.cpp | 7 +++++++ 7 files changed, 27 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index bf9710857a..177095c673 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -10,6 +10,7 @@ // +#include #include "RenderableEntityItem.h" namespace render { @@ -39,6 +40,3 @@ namespace render { } } } - - - diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 1832ef28c3..26d00c9bdb 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -62,5 +62,6 @@ private: \ SimpleRenderableEntityItem _renderHelper; +void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Getters& statusGetters); #endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index d33e52fa6b..32be9707c6 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -18,10 +18,10 @@ #include #include #include -#include #include "EntityTreeRenderer.h" #include "EntitiesRendererLogging.h" +#include "RenderableEntityItem.h" #include "RenderableModelEntityItem.h" EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -180,7 +180,7 @@ namespace render { } } -void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item::Status::Getters& statusGetters) { +void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Getters& statusGetters) { statusGetters.push_back([entity] () -> render::Item::Status::Value { quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index 41cf3b9bbf..ecb16424d6 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -139,6 +139,9 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, _renderItemId = scene->allocateID(); auto renderData = ParticlePayload::Pointer(particlePayload); auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData)); + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(_renderItemId, renderPayload); _scene = scene; return true; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index cd88638e51..a4e20bfcf9 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -549,6 +549,10 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, auto renderData = PolyVoxPayload::Pointer(renderItem); auto renderPayload = std::make_shared(renderData); + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(_myItem, renderPayload); return true; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 62d98f3322..8b2a2d33df 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -19,6 +19,8 @@ #include #include +#include "RenderableEntityItem.h" + // Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1 // is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down. static const float SPHERE_ENTITY_SCALE = 0.5f; @@ -112,6 +114,8 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); _model->addToScene(scene, pendingChanges, false); scene->enqueuePendingChanges(pendingChanges); @@ -203,7 +207,11 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt auto renderData = std::make_shared(self); auto renderPayload = std::make_shared(renderData); - + + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(_myMetaItem, renderPayload); return true; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9785e12736..40fbca26bd 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -198,6 +198,13 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI properties.setAngularVelocityChanged(false); properties.setAccelerationChanged(false); } + + if (wantTerseEditLogging()) { + if (properties.simulationOwnerChanged()) { + QString itemName = entity->getName() != "" ? entity->getName() : entity->getID().toString(); + qCDebug(entities) << "sim ownership for" << itemName << "is now" << senderID; + } + } } // else client accepts what the server says From c2fe937a5c4cc34b6b4b6f6c08ee9b4a28468769 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 10 Nov 2015 17:07:55 -0800 Subject: [PATCH 0843/1003] Removed logging line --- examples/marketplace/dynamicLoader.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/marketplace/dynamicLoader.js b/examples/marketplace/dynamicLoader.js index b40eaa9dba..bc7f4f5fb9 100644 --- a/examples/marketplace/dynamicLoader.js +++ b/examples/marketplace/dynamicLoader.js @@ -30,7 +30,6 @@ function createGrid() { var fbxUrls = urls.filter(function(url) { return url.indexOf('fbx') !== -1; }); - print(JSON.stringify(fbxUrls)); var modelParams = { type: "Model", @@ -78,4 +77,4 @@ function cleanup() { Entities.deleteEntity(model); } -Script.scriptEnding.connect(cleanup); \ No newline at end of file +Script.scriptEnding.connect(cleanup); From 4cf84a4e5b940bc7ac61d3f3d8ed615a9e1590b9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 Nov 2015 17:14:09 -0800 Subject: [PATCH 0844/1003] use makeEntityItemStatusGetters from more entity types --- .../src/RenderableEntityItem.cpp | 36 +++++++++++++++++++ .../src/RenderableModelEntityItem.cpp | 36 ------------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 177095c673..e55ba44487 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -40,3 +40,39 @@ namespace render { } } } + +void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Getters& statusGetters) { + statusGetters.push_back([entity] () -> render::Item::Status::Value { + quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); + const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); + float normalizedDelta = delta * WAIT_THRESHOLD_INV; + // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD + // Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green) + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? + render::Item::Status::Value::GREEN : + render::Item::Status::Value::RED), + (unsigned char) RenderItemStatusIcon::PACKET_RECEIVED); + }); + + statusGetters.push_back([entity] () -> render::Item::Status::Value { + quint64 delta = usecTimestampNow() - entity->getLastBroadcast(); + const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND); + float normalizedDelta = delta * WAIT_THRESHOLD_INV; + // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD + // Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green) + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? + render::Item::Status::Value::MAGENTA : + render::Item::Status::Value::CYAN), + (unsigned char)RenderItemStatusIcon::PACKET_SENT); + }); + + statusGetters.push_back([entity] () -> render::Item::Status::Value { + ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); + if (motionState && motionState->isActive()) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); + } + return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); + }); +} diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index fdbb549cdb..a9cb19e2ff 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -181,42 +181,6 @@ namespace render { } } -void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Getters& statusGetters) { - statusGetters.push_back([entity] () -> render::Item::Status::Value { - quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); - const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); - float normalizedDelta = delta * WAIT_THRESHOLD_INV; - // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD - // Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green) - return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? - render::Item::Status::Value::GREEN : - render::Item::Status::Value::RED), - (unsigned char) RenderItemStatusIcon::PACKET_RECEIVED); - }); - - statusGetters.push_back([entity] () -> render::Item::Status::Value { - quint64 delta = usecTimestampNow() - entity->getLastBroadcast(); - const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND); - float normalizedDelta = delta * WAIT_THRESHOLD_INV; - // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD - // Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green) - return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? - render::Item::Status::Value::MAGENTA : - render::Item::Status::Value::CYAN), - (unsigned char)RenderItemStatusIcon::PACKET_SENT); - }); - - statusGetters.push_back([entity] () -> render::Item::Status::Value { - ObjectMotionState* motionState = static_cast(entity->getPhysicsInfo()); - if (motionState && motionState->isActive()) { - return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE, - (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); - } - return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE, - (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); - }); -} - bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _myMetaItem = scene->allocateID(); From 3f5cd0237d351556d0d9106e801a9d063df3e569 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Tue, 10 Nov 2015 17:29:15 -0800 Subject: [PATCH 0845/1003] Added header to dynamicLoader and removed files that are not ready for merging --- examples/marketplace/dynamicLoader.js | 14 ++++ examples/marketplace/marketplaceSpawner.js | 82 ---------------------- examples/marketplace/modelSwap.js | 41 ----------- 3 files changed, 14 insertions(+), 123 deletions(-) delete mode 100644 examples/marketplace/marketplaceSpawner.js delete mode 100644 examples/marketplace/modelSwap.js diff --git a/examples/marketplace/dynamicLoader.js b/examples/marketplace/dynamicLoader.js index bc7f4f5fb9..674b6e86cd 100644 --- a/examples/marketplace/dynamicLoader.js +++ b/examples/marketplace/dynamicLoader.js @@ -1,3 +1,17 @@ +// +// dynamicLoader.js +// examples +// +// Created by Eric Levin on 11/10/2015. +// Copyright 2013 High Fidelity, Inc. +// +// This is script loads models from a specified directory +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + var BASE_URL = "https://hifi-public.s3.amazonaws.com/"; var models = []; var floorPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));; diff --git a/examples/marketplace/marketplaceSpawner.js b/examples/marketplace/marketplaceSpawner.js deleted file mode 100644 index 6799ed63a3..0000000000 --- a/examples/marketplace/marketplaceSpawner.js +++ /dev/null @@ -1,82 +0,0 @@ -var floorPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));; -floorPosition.y = MyAvatar.position.y - 5; - -Script.include('../libraries/utils.js'); - -var entityScriptURL = Script.resolvePath("modelSwap.js"); - -var modelsToLoad = [{ - lowURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/dojo/dojo_low.fbx", - highURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/dojo/dojo_hi.fbx" -}, { - lowURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_low.fbx", - highURL: "https://s3.amazonaws.com/hifi-public/ozan/3d_marketplace/sets/tuscany/tuscany_hi.fbx" -}]; - -var models = []; - -var floor = Entities.addEntity({ - type: "Model", - modelURL: "https://hifi-public.s3.amazonaws.com/ozan/3d_marketplace/props/floor/3d_mp_floor.fbx", - position: floorPosition, - shapeType: 'box', - dimensions: { - x: 1000, - y: 9, - z: 1000 - } -}); - -//Create grid -var modelParams = { - type: "Model", - dimensions: { - x: 31.85, - y: 7.75, - z: 54.51 - }, - script: entityScriptURL, - userData: JSON.stringify({ - grabbableKey: { - wantsTrigger: true - } - }) - -}; - -var modelPosition = { - x: floorPosition.x + 10, - y: floorPosition.y + 8.5, - z: floorPosition.z - 30 -}; -for (var i = 0; i < modelsToLoad.length; i++) { - modelParams.modelURL = modelsToLoad[i].lowURL; - modelParams.position = modelPosition; - var lowModel = Entities.addEntity(modelParams); - - modelParams.modelURL = modelsToLoad[i].highURL; - modelParams.visible = false; - modelParams.dimensions = Vec3.multiply(modelParams.dimensions, 0.5); - var highModel = Entities.addEntity(modelParams); - models.push({ - low: lowModel, - high: highModel - }); - // customKey, id, data - setEntityCustomData('modelCounterpart', lowModel, {modelCounterpartId: highModel}); - setEntityCustomData('modelCounterpart', highModel, {modelCounterpartId: lowModel}); - - modelPosition.z -= 60; -} - - - -function cleanup() { - Entities.deleteEntity(floor); - models.forEach(function(model) { - Entities.deleteEntity(model.low); - Entities.deleteEntity(model.high); - }); -} - -Script.scriptEnding.connect(cleanup); \ No newline at end of file diff --git a/examples/marketplace/modelSwap.js b/examples/marketplace/modelSwap.js deleted file mode 100644 index 29dbc84e98..0000000000 --- a/examples/marketplace/modelSwap.js +++ /dev/null @@ -1,41 +0,0 @@ -// When user holds down trigger on model for enough time, the model with do a cool animation and swap out with the low or high version of it - - - -(function() { - - var _this; - ModelSwaper = function() { - _this = this; - }; - - ModelSwaper.prototype = { - - startFarTrigger: function() { - print("START TRIGGER") - - //make self invisible and make the model's counterpart visible! - var dimensions = Entities.getEntityProperties(this.entityID, "dimensions").dimensions; - Entities.editEntity(this.entityID, { - visible: false, - dimensions: Vec3.multiply(dimensions, 0.5) - }); - dimensions = Entities.getEntityProperties(this.modelCounterpartId, "dimensions").dimensions; - Entities.editEntity(this.modelCounterpartId, { - visible: true, - dimensions: Vec3.multiply(dimensions, 2) - }); - - }, - - preload: function(entityID) { - this.entityID = entityID; - var props = Entities.getEntityProperties(this.entityID, ["userData"]); - this.modelCounterpartId = JSON.parse(props.userData).modelCounterpart.modelCounterpartId; - } - - }; - - // entity scripts always need to return a newly constructed object of our type - return new ModelSwaper(); -}); \ No newline at end of file From 3557eacbd05d9c3fe28f53e00e6d5ebf41102999 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Nov 2015 18:18:29 -0800 Subject: [PATCH 0846/1003] Add QTryRead/WriteLocker classes --- libraries/shared/src/shared/QTryReadLocker.h | 92 +++++++++++++++++++ libraries/shared/src/shared/QTryWriteLocker.h | 92 +++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 libraries/shared/src/shared/QTryReadLocker.h create mode 100644 libraries/shared/src/shared/QTryWriteLocker.h diff --git a/libraries/shared/src/shared/QTryReadLocker.h b/libraries/shared/src/shared/QTryReadLocker.h new file mode 100644 index 0000000000..573eba61b8 --- /dev/null +++ b/libraries/shared/src/shared/QTryReadLocker.h @@ -0,0 +1,92 @@ +// +// QTryReadLocker.h +// shared/src/shared/QTryReadLocker.h +// +// Created by Clément Brisset on 10/29/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 +// + +#ifndef hifi_QTryReadLocker_h +#define hifi_QTryReadLocker_h + +#include + +class QTryReadLocker { +public: + QTryReadLocker(QReadWriteLock* readWriteLock); + QTryReadLocker(QReadWriteLock* readWriteLock, int timeout); + ~QTryReadLocker(); + + bool isLocked() const; + + void unlock(); + bool tryRelock(); + bool tryRelock(int timeout); + + QReadWriteLock* readWriteLock() const; + +private: + Q_DISABLE_COPY(QTryReadLocker) + quintptr q_val; +}; + +// Implementation +inline QTryReadLocker::QTryReadLocker(QReadWriteLock *areadWriteLock) : + q_val(reinterpret_cast(areadWriteLock)) +{ + Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + "QTryReadLocker", "QTryReadLocker pointer is misaligned"); + tryRelock(); +} + +inline QTryReadLocker::QTryReadLocker(QReadWriteLock *areadWriteLock, int timeout) : + q_val(reinterpret_cast(areadWriteLock)) +{ + Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + "QTryReadLocker", "QTryReadLocker pointer is misaligned"); + tryRelock(timeout); +} + +inline QTryReadLocker::~QTryReadLocker() { + unlock(); +} + +inline bool QTryReadLocker::isLocked() const { + return (q_val & quintptr(1u)) == quintptr(1u); +} + +inline void QTryReadLocker::unlock() { + if (q_val && isLocked()) { + q_val &= ~quintptr(1u); + readWriteLock()->unlock(); + } +} + +inline bool QTryReadLocker::tryRelock() { + if (q_val && !isLocked()) { + if (readWriteLock()->tryLockForRead()) { + q_val |= quintptr(1u); + return true; + } + } + return false; +} + +inline bool QTryReadLocker::tryRelock(int timeout) { + if (q_val && !isLocked()) { + if (readWriteLock()->tryLockForRead(timeout)) { + q_val |= quintptr(1u); + return true; + } + } + return false; +} + +inline QReadWriteLock* QTryReadLocker::readWriteLock() const { + return reinterpret_cast(q_val & ~quintptr(1u)); +} + +#endif // hifi_QTryReadLocker_h \ No newline at end of file diff --git a/libraries/shared/src/shared/QTryWriteLocker.h b/libraries/shared/src/shared/QTryWriteLocker.h new file mode 100644 index 0000000000..319920f724 --- /dev/null +++ b/libraries/shared/src/shared/QTryWriteLocker.h @@ -0,0 +1,92 @@ +// +// QTryWriteLocker.h +// shared/src/shared/QTryWriteLocker.h +// +// Created by Clément Brisset on 10/29/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 +// + +#ifndef hifi_QTryWriteLocker_h +#define hifi_QTryWriteLocker_h + +#include + +class QTryWriteLocker { +public: + QTryWriteLocker(QReadWriteLock* readWriteLock); + QTryWriteLocker(QReadWriteLock* readWriteLock, int timeout); + ~QTryWriteLocker(); + + bool isLocked() const; + + void unlock(); + bool tryRelock(); + bool tryRelock(int timeout); + + QReadWriteLock* readWriteLock() const; + +private: + Q_DISABLE_COPY(QTryWriteLocker) + quintptr q_val; +}; + +// Implementation +inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock *readWriteLock) : + q_val(reinterpret_cast(readWriteLock)) +{ + Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + "QTryWriteLocker", "QTryWriteLocker pointer is misaligned"); + tryRelock(); +} + +inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock *readWriteLock, int timeout) : + q_val(reinterpret_cast(readWriteLock)) +{ + Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + "QTryWriteLocker", "QTryWriteLocker pointer is misaligned"); + tryRelock(timeout); +} + +inline QTryWriteLocker::~QTryWriteLocker() { + unlock(); +} + +inline bool QTryWriteLocker::isLocked() const { + return (q_val & quintptr(1u)) == quintptr(1u); +} + +inline void QTryWriteLocker::unlock() { + if (q_val && isLocked()) { + q_val &= ~quintptr(1u); + readWriteLock()->unlock(); + } +} + +inline bool QTryWriteLocker::tryRelock() { + if (q_val && !isLocked()) { + if (readWriteLock()->tryLockForWrite()) { + q_val |= quintptr(1u); + return true; + } + } + return false; +} + +inline bool QTryWriteLocker::tryRelock(int timeout) { + if (q_val && !isLocked()) { + if (readWriteLock()->tryLockForWrite(timeout)) { + q_val |= quintptr(1u); + return true; + } + } + return false; +} + +inline QReadWriteLock* QTryWriteLocker::readWriteLock() const { + return reinterpret_cast(q_val & ~quintptr(1u)); +} + +#endif // hifi_QTryWriteLocker_h \ No newline at end of file From 7cf2ad22155ddde62a16f660078b2f165ce59081 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Nov 2015 18:19:12 -0800 Subject: [PATCH 0847/1003] Remove lockFor... and expand ReadWriteLockable --- .../shared/src/shared/ReadWriteLockable.h | 136 +++++++++++++----- 1 file changed, 100 insertions(+), 36 deletions(-) diff --git a/libraries/shared/src/shared/ReadWriteLockable.h b/libraries/shared/src/shared/ReadWriteLockable.h index 98dff4a841..04aa20a067 100644 --- a/libraries/shared/src/shared/ReadWriteLockable.h +++ b/libraries/shared/src/shared/ReadWriteLockable.h @@ -9,54 +9,118 @@ #pragma once #ifndef hifi_ReadWriteLockable_h #define hifi_ReadWriteLockable_h + +#include + #include +#include "QTryReadLocker.h" +#include "QTryWriteLocker.h" + class ReadWriteLockable { public: + // Write locks template - bool withWriteLock(F f, bool require = true) const { - if (!require) { - bool result = _lock.tryLockForWrite(); - if (result) { - f(); - _lock.unlock(); - } - return result; - } - - QWriteLocker locker(&_lock); - f(); - return true; - } + bool withWriteLock(F f) const; + + template + bool withWriteLock(F&& f, bool require) const; template - bool withTryWriteLock(F f) const { - return withWriteLock(f, false); - } - + bool withTryWriteLock(F f) const; + template - bool withReadLock(F f, bool require = true) const { - if (!require) { - bool result = _lock.tryLockForRead(); - if (result) { - f(); - _lock.unlock(); - } - return result; - } - - QReadLocker locker(&_lock); - f(); - return true; - } - + bool withTryWriteLock(F f, int timeout) const; + + // Read locks template - bool withTryReadLock(F f) const { - return withReadLock(f, false); - } + bool withReadLock(F f) const; + + template + bool withReadLock(F&& f, bool require) const; + + template + bool withTryReadLock(F f) const; + + template + bool withTryReadLock(F f, int timeout) const; private: mutable QReadWriteLock _lock{ QReadWriteLock::Recursive }; }; +// ReadWriteLockable +template +inline bool ReadWriteLockable::withWriteLock(F f) const { + QWriteLocker locker(&_lock); + f(); + return true; +} + +template +inline bool ReadWriteLockable::withWriteLock(F&& f, bool require) const { + if (require) { + return withWriteLock(std::forward(f)); + } else { + return withTryReadLock(std::forward(f)); + } +} + +template +inline bool ReadWriteLockable::withTryWriteLock(F f) const { + QTryWriteLocker locker(&_lock); + if (locker.isLocked()) { + f(); + return true; + } + return false; +} + +template +inline bool ReadWriteLockable::withTryWriteLock(F f, int timeout) const { + QTryWriteLocker locker(&_lock, timeout); + if (locker.isLocked()) { + f(); + return true; + } + return false; +} + +template +inline bool ReadWriteLockable::withReadLock(F f) const { + QReadLocker locker(&_lock); + f(); + return true; +} + +template +inline bool ReadWriteLockable::withReadLock(F&& f, bool require) const { + if (require) { + return withReadLock(std::forward(f)); + } else { + return withTryReadLock(std::forward(f)); + } +} + +template +inline bool ReadWriteLockable::withTryReadLock(F f) const { + QTryReadLocker locker(&_lock); + if (locker.isLocked()) { + f(); + return true; + } + return false; +} + +template +inline bool ReadWriteLockable::withTryReadLock(F f, int timeout) const { + QTryReadLocker locker(&_lock, timeout); + if (locker.isLocked()) { + f(); + return true; + } + return false; +} + + #endif From 4d8fa58dd3a0c5996e8623b72c2dfd5a894eaa4f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Nov 2015 18:22:30 -0800 Subject: [PATCH 0848/1003] Coding standard --- libraries/shared/src/shared/QTryReadLocker.h | 30 +++++++++---------- libraries/shared/src/shared/QTryWriteLocker.h | 30 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/libraries/shared/src/shared/QTryReadLocker.h b/libraries/shared/src/shared/QTryReadLocker.h index 573eba61b8..a9ab2a1e68 100644 --- a/libraries/shared/src/shared/QTryReadLocker.h +++ b/libraries/shared/src/shared/QTryReadLocker.h @@ -30,22 +30,22 @@ public: private: Q_DISABLE_COPY(QTryReadLocker) - quintptr q_val; + quintptr _val; }; // Implementation -inline QTryReadLocker::QTryReadLocker(QReadWriteLock *areadWriteLock) : - q_val(reinterpret_cast(areadWriteLock)) +inline QTryReadLocker::QTryReadLocker(QReadWriteLock* areadWriteLock) : + _val(reinterpret_cast(areadWriteLock)) { - Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0), "QTryReadLocker", "QTryReadLocker pointer is misaligned"); tryRelock(); } -inline QTryReadLocker::QTryReadLocker(QReadWriteLock *areadWriteLock, int timeout) : - q_val(reinterpret_cast(areadWriteLock)) +inline QTryReadLocker::QTryReadLocker(QReadWriteLock* areadWriteLock, int timeout) : + _val(reinterpret_cast(areadWriteLock)) { - Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0), "QTryReadLocker", "QTryReadLocker pointer is misaligned"); tryRelock(timeout); } @@ -55,20 +55,20 @@ inline QTryReadLocker::~QTryReadLocker() { } inline bool QTryReadLocker::isLocked() const { - return (q_val & quintptr(1u)) == quintptr(1u); + return (_val & quintptr(1u)) == quintptr(1u); } inline void QTryReadLocker::unlock() { - if (q_val && isLocked()) { - q_val &= ~quintptr(1u); + if (_val && isLocked()) { + _val &= ~quintptr(1u); readWriteLock()->unlock(); } } inline bool QTryReadLocker::tryRelock() { - if (q_val && !isLocked()) { + if (_val && !isLocked()) { if (readWriteLock()->tryLockForRead()) { - q_val |= quintptr(1u); + _val |= quintptr(1u); return true; } } @@ -76,9 +76,9 @@ inline bool QTryReadLocker::tryRelock() { } inline bool QTryReadLocker::tryRelock(int timeout) { - if (q_val && !isLocked()) { + if (_val && !isLocked()) { if (readWriteLock()->tryLockForRead(timeout)) { - q_val |= quintptr(1u); + _val |= quintptr(1u); return true; } } @@ -86,7 +86,7 @@ inline bool QTryReadLocker::tryRelock(int timeout) { } inline QReadWriteLock* QTryReadLocker::readWriteLock() const { - return reinterpret_cast(q_val & ~quintptr(1u)); + return reinterpret_cast(_val & ~quintptr(1u)); } #endif // hifi_QTryReadLocker_h \ No newline at end of file diff --git a/libraries/shared/src/shared/QTryWriteLocker.h b/libraries/shared/src/shared/QTryWriteLocker.h index 319920f724..cbfc335da1 100644 --- a/libraries/shared/src/shared/QTryWriteLocker.h +++ b/libraries/shared/src/shared/QTryWriteLocker.h @@ -30,22 +30,22 @@ public: private: Q_DISABLE_COPY(QTryWriteLocker) - quintptr q_val; + quintptr _val; }; // Implementation -inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock *readWriteLock) : - q_val(reinterpret_cast(readWriteLock)) +inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock* readWriteLock) : + _val(reinterpret_cast(readWriteLock)) { - Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0), "QTryWriteLocker", "QTryWriteLocker pointer is misaligned"); tryRelock(); } -inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock *readWriteLock, int timeout) : - q_val(reinterpret_cast(readWriteLock)) +inline QTryWriteLocker::QTryWriteLocker(QReadWriteLock* readWriteLock, int timeout) : + _val(reinterpret_cast(readWriteLock)) { - Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0), + Q_ASSERT_X((_val & quintptr(1u)) == quintptr(0), "QTryWriteLocker", "QTryWriteLocker pointer is misaligned"); tryRelock(timeout); } @@ -55,20 +55,20 @@ inline QTryWriteLocker::~QTryWriteLocker() { } inline bool QTryWriteLocker::isLocked() const { - return (q_val & quintptr(1u)) == quintptr(1u); + return (_val & quintptr(1u)) == quintptr(1u); } inline void QTryWriteLocker::unlock() { - if (q_val && isLocked()) { - q_val &= ~quintptr(1u); + if (_val && isLocked()) { + _val &= ~quintptr(1u); readWriteLock()->unlock(); } } inline bool QTryWriteLocker::tryRelock() { - if (q_val && !isLocked()) { + if (_val && !isLocked()) { if (readWriteLock()->tryLockForWrite()) { - q_val |= quintptr(1u); + _val |= quintptr(1u); return true; } } @@ -76,9 +76,9 @@ inline bool QTryWriteLocker::tryRelock() { } inline bool QTryWriteLocker::tryRelock(int timeout) { - if (q_val && !isLocked()) { + if (_val && !isLocked()) { if (readWriteLock()->tryLockForWrite(timeout)) { - q_val |= quintptr(1u); + _val |= quintptr(1u); return true; } } @@ -86,7 +86,7 @@ inline bool QTryWriteLocker::tryRelock(int timeout) { } inline QReadWriteLock* QTryWriteLocker::readWriteLock() const { - return reinterpret_cast(q_val & ~quintptr(1u)); + return reinterpret_cast(_val & ~quintptr(1u)); } #endif // hifi_QTryWriteLocker_h \ No newline at end of file From d5c44466c4af7d281d630557df9b41d7aaf13b76 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Nov 2015 18:24:41 -0800 Subject: [PATCH 0849/1003] Take Callables with rvalue ref --- .../shared/src/shared/ReadWriteLockable.h | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/shared/src/shared/ReadWriteLockable.h b/libraries/shared/src/shared/ReadWriteLockable.h index 04aa20a067..9279628e10 100644 --- a/libraries/shared/src/shared/ReadWriteLockable.h +++ b/libraries/shared/src/shared/ReadWriteLockable.h @@ -21,37 +21,37 @@ class ReadWriteLockable { public: // Write locks template - bool withWriteLock(F f) const; + bool withWriteLock(F&& f) const; template bool withWriteLock(F&& f, bool require) const; template - bool withTryWriteLock(F f) const; + bool withTryWriteLock(F&& f) const; template - bool withTryWriteLock(F f, int timeout) const; + bool withTryWriteLock(F&& f, int timeout) const; // Read locks template - bool withReadLock(F f) const; + bool withReadLock(F&& f) const; template bool withReadLock(F&& f, bool require) const; template - bool withTryReadLock(F f) const; + bool withTryReadLock(F&& f) const; template - bool withTryReadLock(F f, int timeout) const; + bool withTryReadLock(F&& f, int timeout) const; private: - mutable QReadWriteLock _lock{ QReadWriteLock::Recursive }; + mutable QReadWriteLock _lock { QReadWriteLock::Recursive }; }; // ReadWriteLockable template -inline bool ReadWriteLockable::withWriteLock(F f) const { +inline bool ReadWriteLockable::withWriteLock(F&& f) const { QWriteLocker locker(&_lock); f(); return true; @@ -67,7 +67,7 @@ inline bool ReadWriteLockable::withWriteLock(F&& f, bool require) const { } template -inline bool ReadWriteLockable::withTryWriteLock(F f) const { +inline bool ReadWriteLockable::withTryWriteLock(F&& f) const { QTryWriteLocker locker(&_lock); if (locker.isLocked()) { f(); @@ -77,7 +77,7 @@ inline bool ReadWriteLockable::withTryWriteLock(F f) const { } template -inline bool ReadWriteLockable::withTryWriteLock(F f, int timeout) const { +inline bool ReadWriteLockable::withTryWriteLock(F&& f, int timeout) const { QTryWriteLocker locker(&_lock, timeout); if (locker.isLocked()) { f(); @@ -87,7 +87,7 @@ inline bool ReadWriteLockable::withTryWriteLock(F f, int timeout) const { } template -inline bool ReadWriteLockable::withReadLock(F f) const { +inline bool ReadWriteLockable::withReadLock(F&& f) const { QReadLocker locker(&_lock); f(); return true; @@ -103,7 +103,7 @@ inline bool ReadWriteLockable::withReadLock(F&& f, bool require) const { } template -inline bool ReadWriteLockable::withTryReadLock(F f) const { +inline bool ReadWriteLockable::withTryReadLock(F&& f) const { QTryReadLocker locker(&_lock); if (locker.isLocked()) { f(); @@ -113,7 +113,7 @@ inline bool ReadWriteLockable::withTryReadLock(F f) const { } template -inline bool ReadWriteLockable::withTryReadLock(F f, int timeout) const { +inline bool ReadWriteLockable::withTryReadLock(F&& f, int timeout) const { QTryReadLocker locker(&_lock, timeout); if (locker.isLocked()) { f(); From 65e9dc4be556fc58abdc48bc77983a92465d1b79 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 10 Nov 2015 18:39:59 -0800 Subject: [PATCH 0850/1003] Fixup return types --- libraries/shared/src/shared/ReadWriteLockable.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/shared/src/shared/ReadWriteLockable.h b/libraries/shared/src/shared/ReadWriteLockable.h index 9279628e10..07b46bb92a 100644 --- a/libraries/shared/src/shared/ReadWriteLockable.h +++ b/libraries/shared/src/shared/ReadWriteLockable.h @@ -21,7 +21,7 @@ class ReadWriteLockable { public: // Write locks template - bool withWriteLock(F&& f) const; + void withWriteLock(F&& f) const; template bool withWriteLock(F&& f, bool require) const; @@ -34,7 +34,7 @@ public: // Read locks template - bool withReadLock(F&& f) const; + void withReadLock(F&& f) const; template bool withReadLock(F&& f, bool require) const; @@ -51,16 +51,16 @@ private: // ReadWriteLockable template -inline bool ReadWriteLockable::withWriteLock(F&& f) const { +inline void ReadWriteLockable::withWriteLock(F&& f) const { QWriteLocker locker(&_lock); f(); - return true; } template inline bool ReadWriteLockable::withWriteLock(F&& f, bool require) const { if (require) { - return withWriteLock(std::forward(f)); + withWriteLock(std::forward(f)); + return true; } else { return withTryReadLock(std::forward(f)); } @@ -87,16 +87,16 @@ inline bool ReadWriteLockable::withTryWriteLock(F&& f, int timeout) const { } template -inline bool ReadWriteLockable::withReadLock(F&& f) const { +inline void ReadWriteLockable::withReadLock(F&& f) const { QReadLocker locker(&_lock); f(); - return true; } template inline bool ReadWriteLockable::withReadLock(F&& f, bool require) const { if (require) { - return withReadLock(std::forward(f)); + withReadLock(std::forward(f)); + return true; } else { return withTryReadLock(std::forward(f)); } From b38c1cb30f9fce0f9fe24430ceadb6390697af7c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 08:01:21 -0800 Subject: [PATCH 0851/1003] add icons --- interface/resources/icons/statusIconAtlas.svg | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 interface/resources/icons/statusIconAtlas.svg diff --git a/interface/resources/icons/statusIconAtlas.svg b/interface/resources/icons/statusIconAtlas.svg new file mode 100644 index 0000000000..3e15da10cb --- /dev/null +++ b/interface/resources/icons/statusIconAtlas.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + From c2b0ccd6b395ec6a5729adfc17f0cac15f98a4d1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 08:30:04 -0800 Subject: [PATCH 0852/1003] show simulation ownership with status icons --- .../src/RenderableEntityItem.cpp | 13 ++++++++++++ .../src/RenderableEntityItem.h | 21 +++++++++---------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index e55ba44487..ea6f004b30 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -75,4 +75,17 @@ void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Gette return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE, (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); }); + + statusGetters.push_back([entity] () -> render::Item::Status::Value { + auto nodeList = DependencyManager::get(); + const QUuid& myNodeID = nodeList->getSessionUUID(); + bool weOwnSimulation = entity->getSimulationOwner().matchesValidID(myNodeID); + + if (weOwnSimulation) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::SIMULATION_OWNER); + } + return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE, + (unsigned char)RenderItemStatusIcon::SIMULATION_OWNER); + }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 254a76d291..4da8ff1b4c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -18,12 +18,11 @@ // These or the icon "name" used by the render item status value, they correspond to the atlas texture used by the DrawItemStatus // job in the current rendering pipeline defined as of now (11/2015) in render-utils/RenderDeferredTask.cpp. enum class RenderItemStatusIcon { - PACKET_RECEIVED = 2, - PACKET_SENT = 1, - ACTIVE_IN_BULLET = 0, - SIMULATION_OWNER = 3, - - NONE = 255 + ACTIVE_IN_BULLET = 0, + PACKET_SENT = 1, + PACKET_RECEIVED = 2, + SIMULATION_OWNER = 3, + NONE = 255 }; @@ -32,7 +31,7 @@ public: RenderableEntityItemProxy(EntityItemPointer entity) : entity(entity) { } typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - + EntityItemPointer entity; }; @@ -47,19 +46,19 @@ class SimpleRenderableEntityItem { public: bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _myItem = scene->allocateID(); - + auto renderData = std::make_shared(self); auto renderPayload = std::make_shared(renderData); - + pendingChanges.resetItem(_myItem, renderPayload); - + return true; } void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { pendingChanges.removeItem(_myItem); } - + private: render::ItemID _myItem; }; From 31ebe5dba1f210203705e249b4e01f1296fadf6c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 08:51:10 -0800 Subject: [PATCH 0853/1003] terse logging for sim ownership --- libraries/entities/src/EntityItem.cpp | 24 ++++++++++++++++++++++-- libraries/entities/src/EntityItem.h | 2 ++ libraries/entities/src/EntityTree.cpp | 15 ++++++--------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index dbea43d514..8aa2bdb5d3 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -632,6 +632,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + if (wantTerseEditLogging()) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner; + } } } { // When we own the simulation we don't accept updates to the entity's transform/velocities @@ -996,6 +999,11 @@ EntityTreePointer EntityItem::getTree() const { return tree; } +bool EntityItem::wantTerseEditLogging() { + EntityTreePointer tree = getTree(); + return tree ? tree->wantTerseEditLogging() : false; +} + glm::mat4 EntityItem::getEntityToWorldMatrix() const { glm::mat4 translation = glm::translate(getPosition()); glm::mat4 rotation = glm::mat4_cast(getRotation()); @@ -1492,21 +1500,33 @@ void EntityItem::updateCreated(uint64_t value) { } void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) { - _simulationOwner.set(id, priority); + if (_simulationOwner.set(id, priority)) { + if (wantTerseEditLogging()) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id; + } + } } void EntityItem::setSimulationOwner(const SimulationOwner& owner) { - _simulationOwner.set(owner); + if (_simulationOwner.set(owner)) { + if (wantTerseEditLogging()) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; + } + } } void EntityItem::updateSimulatorID(const QUuid& value) { if (_simulationOwner.setID(value)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; + if (wantTerseEditLogging()) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value; + } } } void EntityItem::clearSimulationOwnership() { _simulationOwner.clear(); + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership() // is only ever called entity-server-side and the flags are only used client-side //_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 858dc7e326..b3c976c1ca 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -307,6 +307,7 @@ public: QString getName() const { return _name; } void setName(const QString& value) { _name = value; } + QString getDebugName() { return _name != "" ? _name : getID().toString(); } bool getVisible() const { return _visible; } void setVisible(bool value) { _visible = value; } @@ -381,6 +382,7 @@ public: void setPhysicsInfo(void* data) { _physicsInfo = data; } EntityTreeElementPointer getElement() const { return _element; } EntityTreePointer getTree() const; + bool wantTerseEditLogging(); static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; } static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 40fbca26bd..179e68ea29 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -199,12 +199,11 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI properties.setAccelerationChanged(false); } - if (wantTerseEditLogging()) { - if (properties.simulationOwnerChanged()) { - QString itemName = entity->getName() != "" ? entity->getName() : entity->getID().toString(); - qCDebug(entities) << "sim ownership for" << itemName << "is now" << senderID; - } - } + // if (wantTerseEditLogging()) { + // if (properties.simulationOwnerChanged()) { + // qCDebug(entities) << "sim ownership for" << entity->getDebugName() << "is now" << senderID; + // } + // } } // else client accepts what the server says @@ -764,9 +763,7 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi if (wantTerseEditLogging()) { QList changedProperties = properties.listChangedProperties(); fixupTerseEditLogging(properties, changedProperties); - QString itemName = - existingEntity->getName() != "" ? existingEntity->getName() : entityItemID.toString(); - qCDebug(entities) << "edit" << itemName << changedProperties; + qCDebug(entities) << "edit" << existingEntity->getDebugName() << changedProperties; } endLogging = usecTimestampNow(); From a76385808041f4e6a3dc2b9576c242176bed9668 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 11 Nov 2015 11:22:05 -0800 Subject: [PATCH 0854/1003] Revert cache-extract This reverts these commits: * 82b26b7 Code convention fixes * 9a484ff Fixes for windows * 086b064 Dumps all urls extracted to stdout. * c002888 Added cache extractor to the tools directory --- tools/CMakeLists.txt | 3 - tools/cache-extract/CMakeLists.txt | 7 -- tools/cache-extract/src/CacheExtractApp.cpp | 130 -------------------- tools/cache-extract/src/CacheExtractApp.h | 47 ------- tools/cache-extract/src/main.cpp | 17 --- 5 files changed, 204 deletions(-) delete mode 100644 tools/cache-extract/CMakeLists.txt delete mode 100644 tools/cache-extract/src/CacheExtractApp.cpp delete mode 100644 tools/cache-extract/src/CacheExtractApp.h delete mode 100644 tools/cache-extract/src/main.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 9bc7031720..2056044a4b 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -10,6 +10,3 @@ set_target_properties(udt-test PROPERTIES FOLDER "Tools") add_subdirectory(vhacd-util) set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") - -add_subdirectory(cache-extract) -set_target_properties(cache-extract PROPERTIES FOLDER "Tools") diff --git a/tools/cache-extract/CMakeLists.txt b/tools/cache-extract/CMakeLists.txt deleted file mode 100644 index 1aaa4d9d04..0000000000 --- a/tools/cache-extract/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(TARGET_NAME cache-extract) - -setup_hifi_project() - -link_hifi_libraries() -copy_dlls_beside_windows_executable() - diff --git a/tools/cache-extract/src/CacheExtractApp.cpp b/tools/cache-extract/src/CacheExtractApp.cpp deleted file mode 100644 index 0cfaef3f83..0000000000 --- a/tools/cache-extract/src/CacheExtractApp.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// -// CacheExtractApp.cpp -// tools/cache-extract/src -// -// Created by Anthony Thibault on 11/6/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 -// - -#include "CacheExtractApp.h" -#include -#include -#include -#include -#include - -// extracted from qnetworkdiskcache.cpp -#define CACHE_VERSION 8 -enum { - CacheMagic = 0xe8, - CurrentCacheVersion = CACHE_VERSION -}; - -CacheExtractApp::CacheExtractApp(int& argc, char** argv) : - QCoreApplication(argc, argv) -{ - QString myDataLoc = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - int lastSlash = myDataLoc.lastIndexOf("/"); - QString cachePath = myDataLoc.leftRef(lastSlash).toString() + "/" + - "High Fidelity" + "/" + "Interface" + "/" + - "data" + QString::number(CACHE_VERSION) + "/"; - - QString outputPath = myDataLoc.leftRef(lastSlash).toString() + "/" + - "High Fidelity" + "/" + "Interface" + "/" + "extracted"; - - qDebug() << "Searching cachePath = " << cachePath << "..."; - - // build list of files - QList fileList; - QDir dir(cachePath); - dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); - QFileInfoList list = dir.entryInfoList(); - for (int i = 0; i < list.size(); ++i) { - QFileInfo fileInfo = list.at(i); - if (fileInfo.isDir()) { - QDir subDir(fileInfo.filePath()); - subDir.setFilter(QDir::Files); - QFileInfoList subList = subDir.entryInfoList(); - for (int j = 0; j < subList.size(); ++j) { - fileList << subList.at(j).filePath(); - } - } - } - - // dump each cache file into the outputPath - for (int i = 0; i < fileList.size(); ++i) { - QByteArray contents; - MyMetaData metaData; - if (extractFile(fileList.at(i), metaData, contents)) { - QString outFileName = outputPath + metaData.url.path(); - int lastSlash = outFileName.lastIndexOf("/"); - QString outDirName = outFileName.leftRef(lastSlash).toString(); - QDir dir; - dir.mkpath(outDirName); - QFile out(outFileName); - if (out.open(QIODevice::WriteOnly)) { - out.write(contents); - out.close(); - qDebug().noquote() << metaData.url.toDisplayString(); - } - else { - qCritical() << "Error opening outputFile = " << outFileName; - } - } else { - qCritical() << "Error extracting = " << fileList.at(i); - } - } - - QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); -} - -bool CacheExtractApp::extractFile(const QString& filePath, MyMetaData& metaData, QByteArray& data) const { - QFile f(filePath); - if (!f.open(QIODevice::ReadOnly)) { - qDebug() << "error opening " << filePath; - return false; - } - QDataStream in(&f); - // from qnetworkdiskcache.cpp QCacheItem::read() - qint32 marker, version, streamVersion; - in >> marker; - if (marker != CacheMagic) { - return false; - } - in >> version; - if (version != CurrentCacheVersion) { - return false; - } - in >> streamVersion; - if (streamVersion > in.version()) - return false; - in.setVersion(streamVersion); - - bool compressed; - in >> metaData; - in >> compressed; - if (compressed) { - QByteArray compressedData; - in >> compressedData; - QBuffer buffer; - buffer.setData(qUncompress(compressedData)); - buffer.open(QBuffer::ReadOnly); - data = buffer.readAll(); - } else { - data = f.readAll(); - } - return true; -} - -QDataStream &operator>>(QDataStream& in, MyMetaData& metaData) { - in >> metaData.url; - in >> metaData.expirationDate; - in >> metaData.lastModified; - in >> metaData.saveToDisk; - in >> metaData.attributes; - in >> metaData.rawHeaders; - return in; -} diff --git a/tools/cache-extract/src/CacheExtractApp.h b/tools/cache-extract/src/CacheExtractApp.h deleted file mode 100644 index 3b34ff891d..0000000000 --- a/tools/cache-extract/src/CacheExtractApp.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// CacheExtractApp.h -// tools/cache-extract/src -// -// Created by Anthony Thibault on 11/6/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 -// - -#ifndef hifi_CacheExtractApp_h -#define hifi_CacheExtractApp_h - -#include -#include -#include -#include -#include -#include - -// copy of QNetworkCacheMetaData -class MyMetaData { -public: - using RawHeader = QPair; - using RawHeaderList = QList; - using AttributesMap = QHash; - - QUrl url; - QDateTime expirationDate; - QDateTime lastModified; - bool saveToDisk; - AttributesMap attributes; - RawHeaderList rawHeaders; -}; - -QDataStream &operator>>(QDataStream& in, MyMetaData& metaData); - -class CacheExtractApp : public QCoreApplication { - Q_OBJECT -public: - CacheExtractApp(int& argc, char** argv); - - bool extractFile(const QString& filePath, MyMetaData& metaData, QByteArray& data) const; -}; - -#endif // hifi_CacheExtractApp_h diff --git a/tools/cache-extract/src/main.cpp b/tools/cache-extract/src/main.cpp deleted file mode 100644 index 71a364ed3e..0000000000 --- a/tools/cache-extract/src/main.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// -// main.cpp -// tools/cache-extract/src -// -// Created by Anthony Thibault on 11/6/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 - -#include -#include "CacheExtractApp.h" - -int main (int argc, char** argv) { - CacheExtractApp app(argc, argv); - return app.exec(); -} From f07d5d9d3f290f161171d33f659e3d02ee4ffb6e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 11:32:12 -0800 Subject: [PATCH 0855/1003] show network and physics status on boxes and spheres --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 2 +- libraries/entities-renderer/src/RenderableEntityItem.h | 8 ++++++-- .../entities-renderer/src/RenderableModelEntityItem.cpp | 4 ++-- .../src/RenderableParticleEffectEntityItem.cpp | 2 +- .../entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 2 +- .../entities-renderer/src/RenderableZoneEntityItem.cpp | 4 ++-- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index ea6f004b30..5013f4d6f8 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -41,7 +41,7 @@ namespace render { } } -void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Getters& statusGetters) { +void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) { statusGetters.push_back([entity] () -> render::Item::Status::Value { quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 4da8ff1b4c..ce5f2d49fa 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -25,6 +25,8 @@ enum class RenderItemStatusIcon { NONE = 255 }; +void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters); + class RenderableEntityItemProxy { public: @@ -50,6 +52,10 @@ public: auto renderData = std::make_shared(self); auto renderPayload = std::make_shared(renderData); + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(self, statusGetters); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(_myItem, renderPayload); return true; @@ -72,6 +78,4 @@ private: \ SimpleRenderableEntityItem _renderHelper; -void makeEntityItemStatusGetters(EntityItem* entity, render::Item::Status::Getters& statusGetters); - #endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a9cb19e2ff..f1be8611e1 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -192,7 +192,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p if (_model) { render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(this, statusGetters); + makeEntityItemStatusGetters(shared_from_this(), statusGetters); // note: we don't care if the model fails to add items, we always added our meta item and therefore we return // true so that the system knows our meta item is in the scene! @@ -236,7 +236,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) { _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(this, statusGetters); + makeEntityItemStatusGetters(shared_from_this(), statusGetters); _model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull); scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp index ecb16424d6..05fca343fd 100644 --- a/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableParticleEffectEntityItem.cpp @@ -140,7 +140,7 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self, auto renderData = ParticlePayload::Pointer(particlePayload); auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData)); render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(this, statusGetters); + makeEntityItemStatusGetters(shared_from_this(), statusGetters); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(_renderItemId, renderPayload); _scene = scene; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index a4e20bfcf9..2e8b8920ce 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -550,7 +550,7 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self, auto renderPayload = std::make_shared(renderData); render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(this, statusGetters); + makeEntityItemStatusGetters(shared_from_this(), statusGetters); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(_myItem, renderPayload); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 8b2a2d33df..b7b91c9b3a 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -115,7 +115,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) { render::PendingChanges pendingChanges; _model->removeFromScene(scene, pendingChanges); render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(this, statusGetters); + makeEntityItemStatusGetters(shared_from_this(), statusGetters); _model->addToScene(scene, pendingChanges, false); scene->enqueuePendingChanges(pendingChanges); @@ -209,7 +209,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt auto renderPayload = std::make_shared(renderData); render::Item::Status::Getters statusGetters; - makeEntityItemStatusGetters(this, statusGetters); + makeEntityItemStatusGetters(shared_from_this(), statusGetters); renderPayload->addStatusGetters(statusGetters); pendingChanges.resetItem(_myMetaItem, renderPayload); From f40e46957bea804287ca1925de2df531976e90dc Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 10 Nov 2015 17:18:34 -0800 Subject: [PATCH 0856/1003] handControlledHead.js added --- .../avatarcontrol/handControlledHead.js | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 examples/example/avatarcontrol/handControlledHead.js diff --git a/examples/example/avatarcontrol/handControlledHead.js b/examples/example/avatarcontrol/handControlledHead.js new file mode 100644 index 0000000000..87c24e9a7e --- /dev/null +++ b/examples/example/avatarcontrol/handControlledHead.js @@ -0,0 +1,65 @@ +// handControlledHead.js + +//This script allows you to look around, driving the rotation of the avatar's head by the right hand orientation. + +const YAW_MULTIPLIER = 20000; +const PITCH_MULTIPLIER = 15000; +const EPSILON = 0.001; +var firstPress = true; +var handPreviousVerticalRotation = 0.0; +var handCurrentVerticalRotation = 0.0; +var handPreviousHorizontalRotation = 0.0; +var handCurrentHorizontalRotation = 0.0; +var rotatedHandPosition; +var rotatedTipPosition; + +function update(deltaTime) { + if(Controller.getValue(Controller.Standard.RightPrimaryThumb)){ + pitchManager(deltaTime); + }else if(!firstPress){ + firstPress = true; + } + if(firstPress && MyAvatar.headYaw){ + MyAvatar.headYaw -= MyAvatar.headYaw/10; + } + +} + +function pitchManager(deltaTime){ + + rotatedHandPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -MyAvatar.bodyYaw, 0), MyAvatar.getRightHandPosition()); + rotatedTipPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, -MyAvatar.bodyYaw, 0), MyAvatar.getRightHandTipPosition()); + + handCurrentVerticalRotation = Vec3.subtract(rotatedTipPosition, rotatedHandPosition).y; + handCurrentHorizontalRotation = Vec3.subtract(rotatedTipPosition, rotatedHandPosition).x; + + var handCurrentHorizontalRotationFiltered = handCurrentHorizontalRotation; + + //to avoid yaw drift + if((handCurrentHorizontalRotation - handPreviousHorizontalRotation) < EPSILON && (handCurrentHorizontalRotation - handPreviousHorizontalRotation) > -EPSILON){ + handCurrentHorizontalRotationFiltered = handPreviousHorizontalRotation; + } + + if(firstPress){ + handPreviousVerticalRotation = handCurrentVerticalRotation; + handPreviousHorizontalRotation = handCurrentHorizontalRotation; + firstPress = false; + } + + MyAvatar.headPitch += (handCurrentVerticalRotation - handPreviousVerticalRotation)*PITCH_MULTIPLIER*deltaTime; + MyAvatar.headYaw -= (handCurrentHorizontalRotationFiltered - handPreviousHorizontalRotation)*YAW_MULTIPLIER*deltaTime; + + + handPreviousVerticalRotation = handCurrentVerticalRotation; + handPreviousHorizontalRotation = handCurrentHorizontalRotationFiltered; + + +} + +function clean(){ + MyAvatar.headYaw = 0.0; +} + + +Script.update.connect(update); +Script.scriptEnding.connect(clean); \ No newline at end of file From e4b1b54e005384e5bc072824278e1a3bb9bebb46 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Wed, 11 Nov 2015 12:07:54 -0800 Subject: [PATCH 0857/1003] added header --- .../example/avatarcontrol/handControlledHead.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/example/avatarcontrol/handControlledHead.js b/examples/example/avatarcontrol/handControlledHead.js index 87c24e9a7e..95ad1655ab 100644 --- a/examples/example/avatarcontrol/handControlledHead.js +++ b/examples/example/avatarcontrol/handControlledHead.js @@ -1,6 +1,14 @@ -// handControlledHead.js - -//This script allows you to look around, driving the rotation of the avatar's head by the right hand orientation. +// +// handControlledHead.js +// examples +// +// Created by Alessandro Signa on 10/11/15. +// Copyright 2015 High Fidelity, Inc. +// +// This script allows you to look around, driving the rotation of the avatar's head by the right hand orientation. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html const YAW_MULTIPLIER = 20000; const PITCH_MULTIPLIER = 15000; From 866116d2855baf69b25777cde78cbafe4c66741a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 12:13:47 -0800 Subject: [PATCH 0858/1003] more debugging --- .../src/entities/EntityServer.cpp | 11 +++- .../src/octree/OctreeSendThread.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 65 +++++++++++-------- libraries/entities/src/EntityTree.h | 1 + 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index e2941541af..81dabd4dcd 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -84,6 +84,10 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) { quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt(); EntityTreePointer tree = std::static_pointer_cast(_tree); shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt); + if (shouldSendDeletedEntities) { + int elapsed = usecTimestampNow() - deletedEntitiesSentAt; + qDebug() << "shouldSendDeletedEntities to node:" << node->getUUID() << "deletedEntitiesSentAt:" << deletedEntitiesSentAt << "elapsed:" << elapsed; + } } return shouldSendDeletedEntities; @@ -116,6 +120,10 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt); } + if (packetsSent > 0) { + qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of " << totalBytes << " total bytes to node:" << node->getUUID(); + } + // TODO: caller is expecting a packetLength, what if we send more than one packet?? return totalBytes; } @@ -134,9 +142,6 @@ void EntityServer::pruneDeletedEntities() { } } }); - - int EXTRA_SECONDS_TO_KEEP = 4; - earliestLastDeletedEntitiesSent -= USECS_PER_SECOND * EXTRA_SECONDS_TO_KEEP; tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent); } } diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 2696c92253..b94317050c 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -570,7 +570,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus } - if (somethingToSend) { + if (somethingToSend && _myServer->wantsVerboseDebug()) { qDebug() << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval << " maxPacketsPerInterval = " << maxPacketsPerInterval << " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 34b09eb67c..7f350888be 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -25,6 +25,7 @@ #include "RecurseOctreeToMapOperator.h" #include "LogHandler.h" +const quint64 EntityTree::DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 500; EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), @@ -803,21 +804,26 @@ void EntityTree::update() { } bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { - int EXTRA_SECONDS_TO_CONSIDER = 4; - quint64 considerEntitiesSince = sinceTime - (USECS_PER_SECOND * EXTRA_SECONDS_TO_CONSIDER); + quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; // we can probably leverage the ordered nature of QMultiMap to do this quickly... bool hasSomethingNewer = false; - { - QReadLocker locker(&_recentlyDeletedEntitiesLock); - QMultiMap::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin(); - while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) { - if (iterator.key() > considerEntitiesSince) { - hasSomethingNewer = true; - } - ++iterator; + QReadLocker locker(&_recentlyDeletedEntitiesLock); + QMultiMap::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin(); + while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) { + if (iterator.key() > considerEntitiesSince) { + hasSomethingNewer = true; + break; // if we have at least one item, we don't need to keep searching } + ++iterator; + } + + if (hasSomethingNewer) { + int elapsed = usecTimestampNow() - considerEntitiesSince; + int difference = considerEntitiesSince - sinceTime; + qDebug() << "EntityTree::hasEntitiesDeletedSince() sinceTime:" << sinceTime + << "considerEntitiesSince:" << considerEntitiesSince << "elapsed:" << elapsed << "difference:" << difference; } return hasSomethingNewer; @@ -826,14 +832,16 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { // sinceTime is an in/out parameter - it will be side effected with the last time sent out std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, bool& hasMore) { + qDebug() << "EntityTree::encodeEntitiesDeletedSince()"; - int EXTRA_SECONDS_TO_CONSIDER = 4; - quint64 considerEntitiesSince = sinceTime - (USECS_PER_SECOND * EXTRA_SECONDS_TO_CONSIDER); + quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; auto deletesPacket = NLPacket::create(PacketType::EntityErase); + qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // pack in flags OCTREE_PACKET_FLAGS flags = 0; deletesPacket->writePrimitive(flags); + qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // pack in sequence number deletesPacket->writePrimitive(sequenceNumber); @@ -841,11 +849,13 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // pack in timestamp OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); deletesPacket->writePrimitive(now); + qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // figure out where we are now and pack a temporary number of IDs uint16_t numberOfIDs = 0; qint64 numberOfIDsPos = deletesPacket->pos(); deletesPacket->writePrimitive(numberOfIDs); + qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been // deleted since we last sent to this node @@ -869,6 +879,8 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // history for a longer time window, these entities are not "lost". But we haven't yet // found/fixed the underlying issue that caused bad UUIDs to be sent to some users. deletesPacket->write(entityID.toRfc4122()); + qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID; + qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); ++numberOfIDs; // check to make sure we have room for one more ID @@ -898,6 +910,9 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // replace the count for the number of included IDs deletesPacket->seek(numberOfIDsPos); deletesPacket->writePrimitive(numberOfIDs); + qDebug() << " ---- at line:" << __LINE__ <<" deletes packet size:" << deletesPacket->getDataSize(); + + qDebug() << " ---- EntityTree::encodeEntitiesDeletedSince() numberOfIDs:" << numberOfIDs; return deletesPacket; } @@ -905,24 +920,22 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // called by the server when it knows all nodes have been sent deleted packets void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { + quint64 considerSinceTime = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; QSet keysToRemove; + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + QMultiMap::iterator iterator = _recentlyDeletedEntityItemIDs.begin(); - { - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - QMultiMap::iterator iterator = _recentlyDeletedEntityItemIDs.begin(); - - // First find all the keys in the map that are older and need to be deleted - while (iterator != _recentlyDeletedEntityItemIDs.end()) { - if (iterator.key() <= sinceTime) { - keysToRemove << iterator.key(); - } - ++iterator; + // First find all the keys in the map that are older and need to be deleted + while (iterator != _recentlyDeletedEntityItemIDs.end()) { + if (iterator.key() <= considerSinceTime) { + keysToRemove << iterator.key(); } + ++iterator; + } - // Now run through the keysToRemove and remove them - foreach (quint64 value, keysToRemove) { - _recentlyDeletedEntityItemIDs.remove(value); - } + // Now run through the keysToRemove and remove them + foreach (quint64 value, keysToRemove) { + _recentlyDeletedEntityItemIDs.remove(value); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index c177840199..dd31901a4d 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -147,6 +147,7 @@ public: void addNewlyCreatedHook(NewlyCreatedEntityHook* hook); void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook); + static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; } bool hasEntitiesDeletedSince(quint64 sinceTime); std::unique_ptr encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, From 040bae60142ef9ed611510382cae777f64d77450 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 12:31:50 -0800 Subject: [PATCH 0859/1003] more debugging --- libraries/entities/src/EntityTree.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7f350888be..49d50b28ff 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -942,6 +942,7 @@ void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) { + qDebug() << "EntityTree::processEraseMessage()"; withWriteLock([&] { packet.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME)); @@ -959,6 +960,7 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s } QUuid entityID = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + qDebug() << " ---- EntityTree::processEraseMessage() contained ID:" << entityID; EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; @@ -978,6 +980,7 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s // NOTE: Caller must lock the tree before calling this. // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { + qDebug() << "EntityTree::processEraseMessageDetails()"; const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); const unsigned char* dataAt = packetData; size_t packetLength = dataByteArray.size(); @@ -1004,6 +1007,8 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons dataAt += encodedID.size(); processedBytes += encodedID.size(); + qDebug() << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID; + EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; From bdfe304f7aee9f8b6a8b3e6969b8146cea690aaa Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 14:08:15 -0800 Subject: [PATCH 0860/1003] remove some logging --- .../src/entities/EntityServer.cpp | 20 +++++++---- libraries/entities/src/EntityTree.cpp | 35 ++++++++++--------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 81dabd4dcd..493a16fea4 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -84,10 +84,13 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) { quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt(); EntityTreePointer tree = std::static_pointer_cast(_tree); shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt); - if (shouldSendDeletedEntities) { - int elapsed = usecTimestampNow() - deletedEntitiesSentAt; - qDebug() << "shouldSendDeletedEntities to node:" << node->getUUID() << "deletedEntitiesSentAt:" << deletedEntitiesSentAt << "elapsed:" << elapsed; - } + + #ifdef EXTRA_ERASE_DEBUGGING + if (shouldSendDeletedEntities) { + int elapsed = usecTimestampNow() - deletedEntitiesSentAt; + qDebug() << "shouldSendDeletedEntities to node:" << node->getUUID() << "deletedEntitiesSentAt:" << deletedEntitiesSentAt << "elapsed:" << elapsed; + } + #endif } return shouldSendDeletedEntities; @@ -120,9 +123,12 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt); } - if (packetsSent > 0) { - qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of " << totalBytes << " total bytes to node:" << node->getUUID(); - } + #ifdef EXTRA_ERASE_DEBUGGING + if (packetsSent > 0) { + qDebug() << "EntityServer::sendSpecialPackets() sent " << packetsSent << "special packets of " + << totalBytes << " total bytes to node:" << node->getUUID(); + } + #endif // TODO: caller is expecting a packetLength, what if we send more than one packet?? return totalBytes; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 49d50b28ff..077bd1b76b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -25,7 +25,7 @@ #include "RecurseOctreeToMapOperator.h" #include "LogHandler.h" -const quint64 EntityTree::DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 500; +const quint64 EntityTree::DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), @@ -819,12 +819,14 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { ++iterator; } +#ifdef EXTRA_ERASE_DEBUGGING if (hasSomethingNewer) { int elapsed = usecTimestampNow() - considerEntitiesSince; int difference = considerEntitiesSince - sinceTime; qDebug() << "EntityTree::hasEntitiesDeletedSince() sinceTime:" << sinceTime << "considerEntitiesSince:" << considerEntitiesSince << "elapsed:" << elapsed << "difference:" << difference; } +#endif return hasSomethingNewer; } @@ -832,16 +834,12 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { // sinceTime is an in/out parameter - it will be side effected with the last time sent out std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, bool& hasMore) { - qDebug() << "EntityTree::encodeEntitiesDeletedSince()"; - quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; auto deletesPacket = NLPacket::create(PacketType::EntityErase); - qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // pack in flags OCTREE_PACKET_FLAGS flags = 0; deletesPacket->writePrimitive(flags); - qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // pack in sequence number deletesPacket->writePrimitive(sequenceNumber); @@ -849,13 +847,11 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // pack in timestamp OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); deletesPacket->writePrimitive(now); - qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // figure out where we are now and pack a temporary number of IDs uint16_t numberOfIDs = 0; qint64 numberOfIDsPos = deletesPacket->pos(); deletesPacket->writePrimitive(numberOfIDs); - qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); // we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been // deleted since we last sent to this node @@ -879,10 +875,12 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // history for a longer time window, these entities are not "lost". But we haven't yet // found/fixed the underlying issue that caused bad UUIDs to be sent to some users. deletesPacket->write(entityID.toRfc4122()); - qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID; - qDebug() << " ---- at line:" << __LINE__ << " deletes packet size:" << deletesPacket->getDataSize(); ++numberOfIDs; + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID; + #endif + // check to make sure we have room for one more ID if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) { hasFilledPacket = true; @@ -910,9 +908,6 @@ std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_S // replace the count for the number of included IDs deletesPacket->seek(numberOfIDsPos); deletesPacket->writePrimitive(numberOfIDs); - qDebug() << " ---- at line:" << __LINE__ <<" deletes packet size:" << deletesPacket->getDataSize(); - - qDebug() << " ---- EntityTree::encodeEntitiesDeletedSince() numberOfIDs:" << numberOfIDs; return deletesPacket; } @@ -942,7 +937,9 @@ void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode) { - qDebug() << "EntityTree::processEraseMessage()"; + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << "EntityTree::processEraseMessage()"; + #endif withWriteLock([&] { packet.seek(sizeof(OCTREE_PACKET_FLAGS) + sizeof(OCTREE_PACKET_SEQUENCE) + sizeof(OCTREE_PACKET_SENT_TIME)); @@ -960,7 +957,9 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s } QUuid entityID = QUuid::fromRfc4122(packet.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - qDebug() << " ---- EntityTree::processEraseMessage() contained ID:" << entityID; + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << " ---- EntityTree::processEraseMessage() contained ID:" << entityID; + #endif EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; @@ -980,7 +979,9 @@ int EntityTree::processEraseMessage(NLPacket& packet, const SharedNodePointer& s // NOTE: Caller must lock the tree before calling this. // TODO: consider consolidating processEraseMessageDetails() and processEraseMessage() int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode) { - qDebug() << "EntityTree::processEraseMessageDetails()"; + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << "EntityTree::processEraseMessageDetails()"; + #endif const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); const unsigned char* dataAt = packetData; size_t packetLength = dataByteArray.size(); @@ -1007,7 +1008,9 @@ int EntityTree::processEraseMessageDetails(const QByteArray& dataByteArray, cons dataAt += encodedID.size(); processedBytes += encodedID.size(); - qDebug() << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID; + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << " ---- EntityTree::processEraseMessageDetails() contains id:" << entityID; + #endif EntityItemID entityItemID(entityID); entityItemIDsToDelete << entityItemID; From 2f903a9513f43a7e5da301a756e4eccf082359bb Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 14:21:14 -0800 Subject: [PATCH 0861/1003] CR feedback and some cleanup --- assignment-client/src/entities/EntityNodeData.h | 2 -- libraries/entities/src/EntityTree.cpp | 8 +++----- libraries/entities/src/EntityTree.h | 1 - 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/entities/EntityNodeData.h b/assignment-client/src/entities/EntityNodeData.h index 69da9ee14b..0ca0834fef 100644 --- a/assignment-client/src/entities/EntityNodeData.h +++ b/assignment-client/src/entities/EntityNodeData.h @@ -18,8 +18,6 @@ class EntityNodeData : public OctreeQueryNode { public: - EntityNodeData() : OctreeQueryNode() { } - virtual PacketType getMyPacketType() const { return PacketType::EntityData; } quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 077bd1b76b..0174dbe39b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -25,7 +25,7 @@ #include "RecurseOctreeToMapOperator.h" #include "LogHandler.h" -const quint64 EntityTree::DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; +static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), @@ -392,10 +392,8 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) if (getIsServer()) { // set up the deleted entities ID - { - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); - } + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); } if (_simulation) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index dd31901a4d..c177840199 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -147,7 +147,6 @@ public: void addNewlyCreatedHook(NewlyCreatedEntityHook* hook); void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook); - static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; } bool hasEntitiesDeletedSince(quint64 sinceTime); std::unique_ptr encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, From 0f057d722987180d9a47c6c1373201b0e3325de8 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 6 Nov 2015 18:58:50 -0800 Subject: [PATCH 0862/1003] Set clip for ControlledAC --- examples/acScripts/ControlACs.js | 29 +++++++++++++++++++++++++++-- examples/acScripts/ControlledAC.js | 16 +++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index 403c0878cb..fb33dd3178 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -17,17 +17,21 @@ var NAMES = new Array("Craig", "Clement", "Jeff"); // ACs names ordered by IDs ( // Those variables MUST be common to every scripts var controlEntitySize = 0.25; -var controlEntityPosition = { x: 2000 , y: 0, z: 0 }; +var controlEntityPosition = { x: 0, y: 0, z: 0 }; // Script. DO NOT MODIFY BEYOND THIS LINE. Script.include("../libraries/toolBars.js"); +var filename = null; +var fileloaded = null; + var DO_NOTHING = 0; var PLAY = 1; var PLAY_LOOP = 2; var STOP = 3; var SHOW = 4; var HIDE = 5; +var LOAD = 6; var COLORS = []; COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 }; @@ -35,6 +39,7 @@ COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 }; COLORS[STOP] = { red: STOP, green: 0, blue: 0 }; COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 }; COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 }; +COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 }; @@ -53,6 +58,7 @@ var onOffIcon = new Array(); var playIcon = new Array(); var playLoopIcon = new Array(); var stopIcon = new Array(); +var loadIcon = new Array(); setupToolBars(); @@ -104,6 +110,14 @@ function setupToolBars() { alpha: ALPHA_OFF, visible: true }, false); + + loadIcon[i] = toolBars[i].addTool({ + imageURL: TOOL_ICON_URL + "recording-upload.svg", + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: ALPHA_OFF, + visible: true + }, false); nameOverlays.push(Overlays.addOverlay("text", { backgroundColor: { red: 0, green: 0, blue: 0 }, @@ -129,11 +143,13 @@ function sendCommand(id, action) { toolBars[id].setAlpha(ALPHA_ON, playIcon[id]); toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]); toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]); + toolBars[id].setAlpha(ALPHA_ON, loadIcon[id]); } else if (action === HIDE) { toolBars[id].selectTool(onOffIcon[id], true); toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]); toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]); toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]); + toolBars[id].setAlpha(ALPHA_OFF, loadIcon[id]); } else if (toolBars[id].toolSelected(onOffIcon[id])) { return; } @@ -148,6 +164,7 @@ function sendCommand(id, action) { var position = { x: controlEntityPosition.x + id * controlEntitySize, y: controlEntityPosition.y, z: controlEntityPosition.z }; Entities.addEntity({ + name: filename, type: "Box", position: position, dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, @@ -173,6 +190,8 @@ function mousePressEvent(event) { sendCommand(i, PLAY_LOOP); } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { sendCommand(i, STOP); + } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + sendCommand(i, LOAD); } else { // Check individual controls for (i = 0; i < NUM_AC; i++) { @@ -188,6 +207,12 @@ function mousePressEvent(event) { sendCommand(i, PLAY_LOOP); } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { sendCommand(i, STOP); + } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + fileloaded = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); + if (!(fileloaded === "null" || fileloaded === null || fileloaded === "")) { + filename = fileloaded; + sendCommand(i, LOAD); + } } else { } @@ -231,4 +256,4 @@ Controller.mousePressEvent.connect(mousePressEvent); Script.update.connect(update); Script.scriptEnding.connect(scriptEnding); -moveUI(); +moveUI(); \ No newline at end of file diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index 93c71aa1a1..25c2ae72d9 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -22,7 +22,7 @@ var useAvatarModel = true; var id = 0; // Set avatar model URL -Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst?1427169998"; +Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73/4618d52e711fbb34df442b414da767bb.fst?1427170144"; // Set position/orientation/scale here if playFromCurrentLocation is true Avatar.position = { x:1, y: 1, z: 1 }; Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); @@ -30,7 +30,7 @@ Avatar.scale = 1.0; // Those variables MUST be common to every scripts var controlEntitySize = 0.25; -var controlEntityPosition = { x: 2000, y: 0, z: 0 }; +var controlEntityPosition = { x: 0, y: 0, z: 0 }; // Script. DO NOT MODIFY BEYOND THIS LINE. var DO_NOTHING = 0; @@ -39,6 +39,7 @@ var PLAY_LOOP = 2; var STOP = 3; var SHOW = 4; var HIDE = 5; +var LOAD = 6; var COLORS = []; COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 }; @@ -46,6 +47,7 @@ COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 }; COLORS[STOP] = { red: STOP, green: 0, blue: 0 }; COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 }; COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 }; +COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 }; controlEntityPosition.x += id * controlEntitySize; @@ -68,7 +70,9 @@ function setupEntityViewer() { EntityViewer.queryOctree(); } -function getAction(controlEntity) { +function getAction(controlEntity) { + filename = controlEntity.name; + if (controlEntity === null || controlEntity.position.x !== controlEntityPosition.x || controlEntity.position.y !== controlEntityPosition.y || @@ -141,6 +145,12 @@ function update(event) { } Agent.isAvatar = false; break; + case LOAD: + print("Load"); + if(filename !== null) { + Avatar.loadRecording(filename); + } + break; case DO_NOTHING: break; default: From 55d386aaabc040184ae5e9450b7487b47792dc06 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Mon, 9 Nov 2015 18:14:42 -0800 Subject: [PATCH 0863/1003] Fixes: Prompt and userData --- examples/acScripts/ControlACs.js | 13 +++++++------ examples/acScripts/ControlledAC.js | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index fb33dd3178..60b72446bb 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -22,8 +22,8 @@ var controlEntityPosition = { x: 0, y: 0, z: 0 }; // Script. DO NOT MODIFY BEYOND THIS LINE. Script.include("../libraries/toolBars.js"); -var filename = null; -var fileloaded = null; +var clip_url = null; +var input_text = null; var DO_NOTHING = 0; var PLAY = 1; @@ -164,7 +164,8 @@ function sendCommand(id, action) { var position = { x: controlEntityPosition.x + id * controlEntitySize, y: controlEntityPosition.y, z: controlEntityPosition.z }; Entities.addEntity({ - name: filename, + name: "Actor Controller", + userData: clip_url, type: "Box", position: position, dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, @@ -208,9 +209,9 @@ function mousePressEvent(event) { } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { sendCommand(i, STOP); } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { - fileloaded = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); - if (!(fileloaded === "null" || fileloaded === null || fileloaded === "")) { - filename = fileloaded; + input_text = Window.prompt("Insert the url of the clip: ",""); + if(!(input_text === "" || input_text === null)){ + clip_url = input_text; sendCommand(i, LOAD); } } else { diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index 25c2ae72d9..8be1172080 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -12,7 +12,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; // Set the following variables to the values needed -var filename = "/Users/clement/Desktop/recording.hfr"; +var clip_url = "https://dl.dropboxusercontent.com/u/14127429/Clips/recording1.hfr"; var playFromCurrentLocation = true; var useDisplayName = true; var useAttachments = true; @@ -51,7 +51,7 @@ COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 }; controlEntityPosition.x += id * controlEntitySize; -Avatar.loadRecording(filename); +Avatar.loadRecording(clip_url); Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); Avatar.setPlayerUseDisplayName(useDisplayName); @@ -71,7 +71,7 @@ function setupEntityViewer() { } function getAction(controlEntity) { - filename = controlEntity.name; + clip_url = controlEntity.userData; if (controlEntity === null || controlEntity.position.x !== controlEntityPosition.x || @@ -147,8 +147,8 @@ function update(event) { break; case LOAD: print("Load"); - if(filename !== null) { - Avatar.loadRecording(filename); + if(clip_url !== null) { + Avatar.loadRecording(clip_url); } break; case DO_NOTHING: From da2b49a1b04a7f310ffc0001a6205c18cf439991 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Wed, 11 Nov 2015 15:03:19 -0800 Subject: [PATCH 0864/1003] Remove hardcoded url --- examples/acScripts/ControlledAC.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index 8be1172080..4eecc11136 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -9,10 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; // Set the following variables to the values needed -var clip_url = "https://dl.dropboxusercontent.com/u/14127429/Clips/recording1.hfr"; +var clip_url = null; var playFromCurrentLocation = true; var useDisplayName = true; var useAttachments = true; @@ -21,8 +20,6 @@ var useAvatarModel = true; // ID of the agent. Two agents can't have the same ID. var id = 0; -// Set avatar model URL -Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73/4618d52e711fbb34df442b414da767bb.fst?1427170144"; // Set position/orientation/scale here if playFromCurrentLocation is true Avatar.position = { x:1, y: 1, z: 1 }; Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0); From 4fc44b09294e903f22a70126db5222006efe3aba Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 11 Nov 2015 15:19:36 -0800 Subject: [PATCH 0865/1003] Updated header summary to be more specific with how the loading works --- examples/marketplace/dynamicLoader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/marketplace/dynamicLoader.js b/examples/marketplace/dynamicLoader.js index 674b6e86cd..27f50aa9cd 100644 --- a/examples/marketplace/dynamicLoader.js +++ b/examples/marketplace/dynamicLoader.js @@ -5,7 +5,8 @@ // Created by Eric Levin on 11/10/2015. // Copyright 2013 High Fidelity, Inc. // -// This is script loads models from a specified directory +// This script is an example of a way to dynamically load and place models in a grid from a specified s3 directory on the hifi-public bucket. +// The directory can be specified by changing the query string variable on line 19 to the desired relative path. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html From 4fddc86851d4ff595e1994632efe34a8f3bdc330 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 15:26:16 -0800 Subject: [PATCH 0866/1003] tweaks to click --- .../input-plugins/src/input-plugins/KeyboardMouseDevice.cpp | 5 +++-- .../input-plugins/src/input-plugins/KeyboardMouseDevice.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 6a08a50b13..9a9514db1b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -64,8 +64,8 @@ void KeyboardMouseDevice::mousePressEvent(QMouseEvent* event, unsigned int devic // key pressed again ? without catching the release event ? } _lastCursor = event->pos(); - _mousePressAt = event->pos(); _mousePressTime = usecTimestampNow(); + _mouseMoved = false; eraseMouseClicked(); } @@ -78,7 +78,7 @@ void KeyboardMouseDevice::mouseReleaseEvent(QMouseEvent* event, unsigned int dev // input for this button we might want to add some small tolerance to this so if you do a small drag it // till counts as a clicked. static const int CLICK_TIME = USECS_PER_MSEC * 500; // 500 ms to click - if (_mousePressAt == event->pos() && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) { + if (!_mouseMoved && (usecTimestampNow() - _mousePressTime < CLICK_TIME)) { _inputDevice->_buttonPressedMap.insert(_inputDevice->makeInput((Qt::MouseButton) event->button(), true).getChannel()); } } @@ -100,6 +100,7 @@ void KeyboardMouseDevice::mouseMoveEvent(QMouseEvent* event, unsigned int device _inputDevice->_axisStateMap[MOUSE_AXIS_Y_NEG] = (currentMove.y() > 0 ? currentMove.y() : 0.0f); _lastCursor = currentPos; + _mouseMoved = true; eraseMouseClicked(); } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 7182424df0..b31c59d11a 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -115,8 +115,8 @@ public: protected: QPoint _lastCursor; - QPoint _mousePressAt; quint64 _mousePressTime; + bool _mouseMoved; glm::vec2 _lastTouch; std::shared_ptr _inputDevice { std::make_shared() }; From d36cc1556ab869f0732a04b250547814aca595cf Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 15:32:07 -0800 Subject: [PATCH 0867/1003] switch back context menu to secondary thumb --- examples/libraries/omniTool.js | 2 +- interface/resources/controllers/standard.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index e0e5da4496..4c995d6528 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -64,7 +64,7 @@ OmniTool = function(left) { }); this.mapping = Controller.newMapping(); - this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightSecondaryThumb).to(function(value){ + this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb).to(function(value){ that.onUpdateTrigger(value); }) this.mapping.enable(); diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 4833388080..d4988fc00d 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -33,7 +33,7 @@ { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, { "from": "Standard.LeftSecondaryThumb", "to": "Actions.CycleCamera" }, - { "from": "Standard.RightPrimaryThumb", "to": "Actions.ContextMenu" }, + { "from": "Standard.RightSecondaryThumb", "to": "Actions.ContextMenu" }, { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, From 56dc9092e0ec8df78576be16b4af64dbe13aa71d Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 11 Nov 2015 15:52:12 -0800 Subject: [PATCH 0868/1003] added s3 server --- examples/marketplace/S3Server/Procfile | 1 + examples/marketplace/S3Server/index.js | 44 ++++++++++++++++++++++ examples/marketplace/S3Server/package.json | 18 +++++++++ 3 files changed, 63 insertions(+) create mode 100644 examples/marketplace/S3Server/Procfile create mode 100644 examples/marketplace/S3Server/index.js create mode 100644 examples/marketplace/S3Server/package.json diff --git a/examples/marketplace/S3Server/Procfile b/examples/marketplace/S3Server/Procfile new file mode 100644 index 0000000000..5ec9cc2c50 --- /dev/null +++ b/examples/marketplace/S3Server/Procfile @@ -0,0 +1 @@ +web: node index.js \ No newline at end of file diff --git a/examples/marketplace/S3Server/index.js b/examples/marketplace/S3Server/index.js new file mode 100644 index 0000000000..e21adb99ad --- /dev/null +++ b/examples/marketplace/S3Server/index.js @@ -0,0 +1,44 @@ +var express = require('express'); +var app = express(); +var AWS = require('aws-sdk'); +var url = require('url'); +var querystring = require('querystring'); +var _ = require('underscore'); + +AWS.config.update({ + region: "us-east-1" +}); + +var s3 = new AWS.S3(); + +app.set('port', (process.env.PORT || 5000)); + +app.get('/', function(req, res) { + var urlParts = url.parse(req.url) + var query = querystring.parse(urlParts.query); + + var params = { + Bucket: "hifi-public", + Marker: query.assetDir, + MaxKeys: 10 + }; + s3.listObjects(params, function(err, data) { + if (err) { + console.log(err, err.stack); + res.send("ERROR") + } else { + var keys = _.pluck(data.Contents, 'Key') + res.send({ + urls: keys + }); + } + }); +}); + + +app.listen(app.get('port'), function() { + console.log('Node app is running on port', app.get('port')); +}) + + +//ozan/3d_marketplace \ No newline at end of file diff --git a/examples/marketplace/S3Server/package.json b/examples/marketplace/S3Server/package.json new file mode 100644 index 0000000000..51a77e7ff9 --- /dev/null +++ b/examples/marketplace/S3Server/package.json @@ -0,0 +1,18 @@ +{ + "name": "s3fileserver", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "eric", + "license": "ISC", + "dependencies": { + "aws-sdk": "^2.2.15", + "express": "^4.13.3", + "querystring": "^0.2.0", + "underscore": "^1.8.3", + "url": "^0.11.0" + } +} \ No newline at end of file From 115b63a1178fedbd28ed98ec446b3f342d625814 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Nov 2015 15:54:48 -0800 Subject: [PATCH 0869/1003] Simplify rotationBetween --- libraries/shared/src/GLMHelpers.cpp | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index dc7887c9f8..b57754066b 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -201,30 +201,7 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2) { // Helper function return the rotation from the first vector onto the second glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { - float angle = angleBetween(v1, v2); - if (glm::isnan(angle) || angle < EPSILON) { - return glm::quat(); - } - glm::vec3 axis; - if (angle > 179.99f * RADIANS_PER_DEGREE) { // 180 degree rotation; must use another axis - axis = glm::cross(v1, glm::vec3(1.0f, 0.0f, 0.0f)); - float axisLength = glm::length(axis); - if (axisLength < EPSILON) { // parallel to x; y will work - axis = glm::normalize(glm::cross(v1, glm::vec3(0.0f, 1.0f, 0.0f))); - } else { - axis /= axisLength; - } - } else { - axis = glm::normalize(glm::cross(v1, v2)); - // It is possible for axis to be nan even when angle is not less than EPSILON. - // For example when angle is small but not tiny but v1 and v2 and have very short lengths. - if (glm::isnan(glm::dot(axis, axis))) { - // set angle and axis to values that will generate an identity rotation - angle = 0.0f; - axis = glm::vec3(1.0f, 0.0f, 0.0f); - } - } - return glm::angleAxis(angle, axis); + return glm::quat(glm::normalize(v1), glm::normalize(v2)); } bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) { From 806d303dcd8b245de1d2093846c6d445d97655e5 Mon Sep 17 00:00:00 2001 From: ericrius1 Date: Wed, 11 Nov 2015 15:55:32 -0800 Subject: [PATCH 0870/1003] Added header file --- examples/marketplace/S3Server/index.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/examples/marketplace/S3Server/index.js b/examples/marketplace/S3Server/index.js index e21adb99ad..ff7eac6cdf 100644 --- a/examples/marketplace/S3Server/index.js +++ b/examples/marketplace/S3Server/index.js @@ -1,3 +1,18 @@ +// +// index.js +// examples +// +// Created by Eric Levin on 11/10/2015. +// Copyright 2013 High Fidelity, Inc. +// +// This is a simple REST API that allows an interface client script to get a list of files paths from an S3 bucket. +// To change your bucket, modify line 34 to your desired bucket. +// Please refer to http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html +// for instructions on how to configure the SDK to work with your bucket. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + var express = require('express'); var app = express(); var AWS = require('aws-sdk'); @@ -40,5 +55,3 @@ app.listen(app.get('port'), function() { console.log('Node app is running on port', app.get('port')); }) - -//ozan/3d_marketplace \ No newline at end of file From e4897a8de77345e08800946c3433f3115cd32cf1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Nov 2015 15:58:27 -0800 Subject: [PATCH 0871/1003] Use new constants --- libraries/script-engine/src/Quat.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp index 14a6573415..bb74b20be0 100644 --- a/libraries/script-engine/src/Quat.cpp +++ b/libraries/script-engine/src/Quat.cpp @@ -64,15 +64,15 @@ glm::quat Quat::inverse(const glm::quat& q) { } glm::vec3 Quat::getFront(const glm::quat& orientation) { - return orientation * IDENTITY_FRONT; + return orientation * Vectors::FRONT; } glm::vec3 Quat::getRight(const glm::quat& orientation) { - return orientation * IDENTITY_RIGHT; + return orientation * Vectors::RIGHT; } glm::vec3 Quat::getUp(const glm::quat& orientation) { - return orientation * IDENTITY_UP; + return orientation * Vectors::UP; } glm::vec3 Quat::safeEulerAngles(const glm::quat& orientation) { From 07f3d8eca04aa1c82a990a32ea7f1520d9fa3d55 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 16:03:08 -0800 Subject: [PATCH 0872/1003] more debugging prints --- .../entities/src/EntityActionInterface.cpp | 21 ++++++++++ .../entities/src/EntityActionInterface.h | 2 + libraries/entities/src/EntityItem.cpp | 38 ++++++++++--------- libraries/entities/src/EntityTree.cpp | 20 ++++------ libraries/entities/src/SimulationOwner.cpp | 2 +- 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index 549aacbd0a..ce9a93a6ac 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -304,3 +304,24 @@ QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType) entityActionType = (EntityActionType)actionTypeAsInt; return stream; } + +QString serializedActionsToDebugString(QByteArray data) { + if (data.size() == 0) { + return QString(); + } + QVector serializedActions; + QDataStream serializedActionsStream(data); + serializedActionsStream >> serializedActions; + + QString result; + foreach(QByteArray serializedAction, serializedActions) { + QDataStream serializedActionStream(serializedAction); + EntityActionType actionType; + QUuid actionID; + serializedActionStream >> actionType; + serializedActionStream >> actionID; + result += EntityActionInterface::actionTypeToString(actionType) + "-" + actionID.toString() + " "; + } + + return result; +} diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index b257df3325..01292e3840 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -89,4 +89,6 @@ typedef std::shared_ptr EntityActionPointer; QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType); QDataStream& operator>>(QDataStream& stream, EntityActionType& entityActionType); +QString serializedActionsToDebugString(QByteArray data); + #endif // hifi_EntityActionInterface_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8aa2bdb5d3..807186a304 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -630,11 +630,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef dataAt += bytes; bytesRead += bytes; + if (wantTerseEditLogging() && _simulationOwner != newSimOwner) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner; + } if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; - if (wantTerseEditLogging()) { - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << newSimOwner; - } } } { // When we own the simulation we don't accept updates to the entity's transform/velocities @@ -740,7 +740,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // this "new" data is actually slightly out of date. We calculate the time we need to skip forward and // use our simulation helper routine to get a best estimate of where the entity should be. float skipTimeForward = (float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND); - + // we want to extrapolate the motion forward to compensate for packet travel time, but // we don't want the side effect of flag setting. simulateKinematicMotion(skipTimeForward, false); @@ -748,7 +748,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (overwriteLocalData) { if (!_simulationOwner.matchesValidID(myNodeID)) { - _lastSimulated = now; } } @@ -1500,33 +1499,36 @@ void EntityItem::updateCreated(uint64_t value) { } void EntityItem::setSimulationOwner(const QUuid& id, quint8 priority) { - if (_simulationOwner.set(id, priority)) { - if (wantTerseEditLogging()) { - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id; - } + if (wantTerseEditLogging() && (id != _simulationOwner.getID() || priority != _simulationOwner.getPriority())) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << id << priority; } + _simulationOwner.set(id, priority); } void EntityItem::setSimulationOwner(const SimulationOwner& owner) { - if (_simulationOwner.set(owner)) { - if (wantTerseEditLogging()) { - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; - } + if (wantTerseEditLogging() && _simulationOwner != owner) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << owner; } + + _simulationOwner.set(owner); } void EntityItem::updateSimulatorID(const QUuid& value) { + if (wantTerseEditLogging() && _simulationOwner.getID() != value) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value; + } + if (_simulationOwner.setID(value)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; - if (wantTerseEditLogging()) { - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now" << value; - } } } void EntityItem::clearSimulationOwnership() { + if (wantTerseEditLogging() && !_simulationOwner.isNull()) { + qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; + } + _simulationOwner.clear(); - qCDebug(entities) << "sim ownership for" << getDebugName() << "is now null"; // don't bother setting the DIRTY_SIMULATOR_ID flag because clearSimulationOwnership() // is only ever called entity-server-side and the flags are only used client-side //_dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; @@ -1669,7 +1671,7 @@ void EntityItem::deserializeActionsInternal() { return; } - EntityTreePointer entityTree = _element ? _element->getTree() : nullptr; + EntityTreePointer entityTree = getTree(); assert(entityTree); EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr; assert(simulation); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 179e68ea29..de97af988d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -197,13 +197,11 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI properties.setVelocityChanged(false); properties.setAngularVelocityChanged(false); properties.setAccelerationChanged(false); - } - // if (wantTerseEditLogging()) { - // if (properties.simulationOwnerChanged()) { - // qCDebug(entities) << "sim ownership for" << entity->getDebugName() << "is now" << senderID; - // } - // } + if (wantTerseEditLogging()) { + qCDebug(entities) << senderNode->getUUID() << "physical edits suppressed"; + } + } } // else client accepts what the server says @@ -666,10 +664,7 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { QByteArray value = properties.getActionData(); - QString changeHint = "0"; - if (value.size() > 0) { - changeHint = "+"; - } + QString changeHint = serializedActionsToDebugString(value); changedProperties[index] = QString("actionData:") + changeHint; } } @@ -763,7 +758,8 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi if (wantTerseEditLogging()) { QList changedProperties = properties.listChangedProperties(); fixupTerseEditLogging(properties, changedProperties); - qCDebug(entities) << "edit" << existingEntity->getDebugName() << changedProperties; + qCDebug(entities) << senderNode->getUUID() << "edit" << + existingEntity->getDebugName() << changedProperties; } endLogging = usecTimestampNow(); @@ -793,7 +789,7 @@ int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* edi if (wantTerseEditLogging()) { QList changedProperties = properties.listChangedProperties(); fixupTerseEditLogging(properties, changedProperties); - qCDebug(entities) << "add" << entityItemID << changedProperties; + qCDebug(entities) << senderNode->getUUID() << "add" << entityItemID << changedProperties; } endLogging = usecTimestampNow(); diff --git a/libraries/entities/src/SimulationOwner.cpp b/libraries/entities/src/SimulationOwner.cpp index d6957873e2..24f6784954 100644 --- a/libraries/entities/src/SimulationOwner.cpp +++ b/libraries/entities/src/SimulationOwner.cpp @@ -157,7 +157,7 @@ void SimulationOwner::test() { } bool SimulationOwner::operator!=(const SimulationOwner& other) { - return (_id != other._id && _priority != other._priority); + return (_id != other._id || _priority != other._priority); } SimulationOwner& SimulationOwner::operator=(const SimulationOwner& other) { From 715a46ee82c2d61ba94fa8b1fc1aef29adb70232 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 16:35:04 -0800 Subject: [PATCH 0873/1003] show actions in status icons --- interface/resources/icons/statusIconAtlas.svg | 1 + .../src/RenderableEntityItem.cpp | 9 ++++ .../src/RenderableEntityItem.h | 1 + libraries/render/src/render/DrawStatus.cpp | 41 +++++++++++++++---- libraries/render/src/render/DrawStatus.h | 3 +- libraries/render/src/render/Scene.cpp | 23 ++++------- libraries/render/src/render/Scene.h | 6 ++- .../render/src/render/drawItemStatus.slv | 19 +++++++-- 8 files changed, 72 insertions(+), 31 deletions(-) diff --git a/interface/resources/icons/statusIconAtlas.svg b/interface/resources/icons/statusIconAtlas.svg index 3e15da10cb..72f9bc4af7 100644 --- a/interface/resources/icons/statusIconAtlas.svg +++ b/interface/resources/icons/statusIconAtlas.svg @@ -27,4 +27,5 @@ c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1 c-5.9,0.1-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2 c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"/> + diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 5013f4d6f8..5504268dce 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -88,4 +88,13 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status: return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE, (unsigned char)RenderItemStatusIcon::SIMULATION_OWNER); }); + + statusGetters.push_back([entity] () -> render::Item::Status::Value { + if (entity->hasActions()) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN, + (unsigned char)RenderItemStatusIcon::HAS_ACTIONS); + } + return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, + (unsigned char)RenderItemStatusIcon::HAS_ACTIONS); + }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index ce5f2d49fa..212b71759f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -22,6 +22,7 @@ enum class RenderItemStatusIcon { PACKET_SENT = 1, PACKET_RECEIVED = 2, SIMULATION_OWNER = 3, + HAS_ACTIONS = 4, NONE = 255 }; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 7b344ae63e..3171b8160c 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -68,7 +68,8 @@ const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() { _drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos"); _drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim"); - _drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus"); + _drawItemStatusValue0Loc = program->getUniforms().findLocation("inStatus0"); + _drawItemStatusValue1Loc = program->getUniforms().findLocation("inStatus1"); auto state = std::make_shared(); @@ -98,6 +99,8 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; + const int NUM_STATUS_VEC4_PER_ITEM = 2; + const int VEC4_LENGTH = 4; // FIrst thing, we collect the bound and the status for all the items we want to render int nbItems = 0; @@ -109,8 +112,8 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex _itemStatus = std::make_shared();; } - _itemBounds->resize((inItems.size() * sizeof(AABox))); - _itemStatus->resize((inItems.size() * sizeof(glm::vec4))); + _itemBounds->resize((inItems.size() * sizeof(AABox))); + _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); for (auto& item : inItems) { @@ -121,11 +124,31 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); } auto& itemScene = scene->getItem(item.id); - (*itemStatus) = itemScene.getStatusPackedValues(); + + auto itemStatusPointer = itemScene.getStatus(); + if (itemStatusPointer) { + // Query the current status values, this is where the statusGetter lambda get called + auto&& currentStatusValues = itemStatusPointer->getCurrentValues(); + int valueNum = 0; + for (int vec4Num = 0; vec4Num < NUM_STATUS_VEC4_PER_ITEM; vec4Num++) { + (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); + for (int component = 0; component < VEC4_LENGTH; component++) { + valueNum = vec4Num * VEC4_LENGTH + component; + if (valueNum < (int)currentStatusValues.size()) { + (*itemStatus)[component] = currentStatusValues[valueNum].getPackedData(); + } + } + itemStatus++; + } + } else { + (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); + itemStatus++; + (*itemStatus) = glm::ivec4(Item::Status::Value::INVALID.getPackedData()); + itemStatus++; + } nbItems++; - itemAABox++; - itemStatus++; + itemAABox++; } } } @@ -170,9 +193,9 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex for (int i = 0; i < nbItems; i++) { batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const float*) (itemAABox + i)); batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const float*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); - batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const int*) (itemStatus + i)); - - batch.draw(gpu::TRIANGLES, 24, 0); + batch._glUniform4iv(_drawItemStatusValue0Loc, 1, (const int*)(itemStatus + NUM_STATUS_VEC4_PER_ITEM * i)); + batch._glUniform4iv(_drawItemStatusValue1Loc, 1, (const int*)(itemStatus + NUM_STATUS_VEC4_PER_ITEM * i + 1)); + batch.draw(gpu::TRIANGLES, 24 * NUM_STATUS_VEC4_PER_ITEM, 0); } } batch.setResourceTexture(0, 0); diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index bb9cb07e4b..83be3112e0 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -21,7 +21,8 @@ namespace render { int _drawItemBoundDimLoc = -1; int _drawItemStatusPosLoc = -1; int _drawItemStatusDimLoc = -1; - int _drawItemStatusValueLoc = -1; + int _drawItemStatusValue0Loc = -1; + int _drawItemStatusValue1Loc = -1; gpu::Stream::FormatPointer _drawItemFormat; gpu::PipelinePointer _drawItemBoundsPipeline; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 2081d5267f..18cf1d8335 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -78,14 +78,14 @@ void Item::Status::Value::setIcon(unsigned char icon) { _icon = icon; } -void Item::Status::getPackedValues(glm::ivec4& values) const { - for (unsigned int i = 0; i < (unsigned int)values.length(); i++) { - if (i < _values.size()) { - values[i] = _values[i]().getPackedData(); - } else { - values[i] = Value::INVALID.getPackedData(); - } +Item::Status::Values Item::Status::getCurrentValues() const { + Values currentValues(_values.size()); + auto currentValue = currentValues.begin(); + for (auto& getter : _values) { + (*currentValue) = getter(); + currentValue++; } + return currentValues; } void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) { @@ -113,15 +113,6 @@ void Item::resetPayload(const PayloadPointer& payload) { } } -glm::ivec4 Item::getStatusPackedValues() const { - glm::ivec4 values(Status::Value::INVALID.getPackedData()); - auto& status = getStatus(); - if (status) { - status->getPackedValues(values); - }; - return values; -} - void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 7176e5753e..6ddd60cce8 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -241,7 +241,10 @@ public: void addGetter(const Getter& getter) { _values.push_back(getter); } - void getPackedValues(glm::ivec4& values) const; + size_t getNumValues() const { return _values.size(); } + + using Values = std::vector ; + Values getCurrentValues() const; }; typedef std::shared_ptr StatusPointer; @@ -305,7 +308,6 @@ public: // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } - glm::ivec4 getStatusPackedValues() const; protected: PayloadPointer _payload; diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index 8c4f0724d5..9974146916 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -21,7 +21,8 @@ out vec3 varTexcoord; uniform vec3 inBoundPos; uniform vec3 inBoundDim; -uniform ivec4 inStatus; +uniform ivec4 inStatus0; +uniform ivec4 inStatus1; vec3 paintRainbow(float normalizedHue) { float v = normalizedHue * 6.f; @@ -44,6 +45,17 @@ vec3 paintRainbow(float normalizedHue) { } } +const int INVALID_STATUS = 0xFFFFFFFF; + +int getIconStatus(int icon) { + if (icon < 4) { + return inStatus0[icon]; + } else if (icon < 8) { + return inStatus1[icon - 4]; + } + return INVALID_STATUS; +} + vec3 unpackStatus(int v) { return vec3(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), clamp(float(uint((v >> 16) & 0xFF)) / 255.0, 0.0, 1.0), @@ -71,9 +83,10 @@ void main(void) { // Which icon are we dealing with ? int iconNum = gl_VertexID / NUM_VERTICES; + int packedIconStatus = getIconStatus(iconNum); // if invalid, just kill - if (inStatus[iconNum] == 0xFFFFFFFF) { + if (packedIconStatus == INVALID_STATUS) { gl_Position = anchorPoint; varColor = vec4(1.0); return; @@ -84,7 +97,7 @@ void main(void) { vec4 quadPos = UNIT_QUAD[twoTriID]; // unpack to get x and y satus - vec3 iconStatus = unpackStatus(inStatus[iconNum]); + vec3 iconStatus = unpackStatus(packedIconStatus); // Use the status for showing a color varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); From be8674820022dca2efdeaa82fd0addcefe722919 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 16:38:28 -0800 Subject: [PATCH 0874/1003] oops --- assignment-client/src/octree/OctreeSendThread.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 96646d9e39..ffcab74b87 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -571,9 +571,9 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus } if (somethingToSend && _myServer->wantsVerboseDebug()) { - qCDebug(otree) << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval - << " maxPacketsPerInterval = " << maxPacketsPerInterval - << " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval; + qCDebug(octree) << "Hit PPS Limit, packetsSentThisInterval =" << packetsSentThisInterval + << " maxPacketsPerInterval = " << maxPacketsPerInterval + << " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval; } // Here's where we can/should allow the server to send other data... From a16a477134380d32e10d7dd289dc488278b140e3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 16:51:55 -0800 Subject: [PATCH 0875/1003] fix warning --- libraries/render/src/render/drawItemStatus.slv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index 9974146916..f078e3fd65 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -45,7 +45,7 @@ vec3 paintRainbow(float normalizedHue) { } } -const int INVALID_STATUS = 0xFFFFFFFF; +const int INVALID_STATUS = int(0xFFFFFFFF); int getIconStatus(int icon) { if (icon < 4) { From 80e15fb77dd12fcef55301280568b5b5902fa0ca Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 11 Nov 2015 17:03:21 -0800 Subject: [PATCH 0876/1003] reduce client to server to client entity edit latency --- assignment-client/src/octree/OctreeServerConsts.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/octree/OctreeServerConsts.h b/assignment-client/src/octree/OctreeServerConsts.h index 02f59f3802..30b55b8786 100644 --- a/assignment-client/src/octree/OctreeServerConsts.h +++ b/assignment-client/src/octree/OctreeServerConsts.h @@ -17,8 +17,14 @@ #include const int MAX_FILENAME_LENGTH = 1024; -const int INTERVALS_PER_SECOND = 60; + +/// This is the frequency (hz) that we check the octree server for changes to determine if we need to +/// send new "scene" information to the viewers. This will directly effect how quickly edits are +/// sent down do viewers. By setting it to 90hz we allow edits happening at 90hz to be sent down +/// to viewers at a rate more closely matching the edit rate. It would probably be better to allow +/// clients to ask the server to send at a rate consistent with their current vsynch since clients +/// can't render any faster than their vsynch even if the server sent them more acurate information +const int INTERVALS_PER_SECOND = 90; const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND; -const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for sending octree elements #endif // hifi_OctreeServerConsts_h From 6a31f76d5f4d6800062461bf39ea2797ddde7f68 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 12 Nov 2015 02:11:07 +0100 Subject: [PATCH 0877/1003] Revert Anchor entity addition --- libraries/entities/src/AnchorEntityItem.cpp | 84 ------------------- libraries/entities/src/AnchorEntityItem.h | 47 ----------- libraries/entities/src/EntityTypes.cpp | 2 - libraries/entities/src/EntityTypes.h | 3 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 - 6 files changed, 2 insertions(+), 137 deletions(-) delete mode 100644 libraries/entities/src/AnchorEntityItem.cpp delete mode 100644 libraries/entities/src/AnchorEntityItem.h diff --git a/libraries/entities/src/AnchorEntityItem.cpp b/libraries/entities/src/AnchorEntityItem.cpp deleted file mode 100644 index d84ac3c3a5..0000000000 --- a/libraries/entities/src/AnchorEntityItem.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// AnchorEntityItem.cpp -// libraries/entities/src -// -// Created by Thijs Wenker on 11/4/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 "EntitiesLogging.h" -#include "EntityItemID.h" -#include "EntityItemProperties.h" -#include "AnchorEntityItem.h" - - -EntityItemPointer AnchorEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer result { new AnchorEntityItem(entityID, properties) }; - return result; -} - -// our non-pure virtual subclass for now... -AnchorEntityItem::AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) : - EntityItem(entityItemID) -{ - _type = EntityTypes::Anchor; - - setProperties(properties); -} - - -EntityItemProperties AnchorEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { - EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - return properties; -} - -bool AnchorEntityItem::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - - if (somethingChanged) { - bool wantDebug = false; - if (wantDebug) { - uint64_t now = usecTimestampNow(); - int elapsed = now - getLastEdited(); - qCDebug(entities) << "AnchorEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << - "now=" << now << " getLastEdited()=" << getLastEdited(); - } - setLastEdited(properties._lastEdited); - } - - return somethingChanged; -} - -int AnchorEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - return bytesRead; -} - - -// TODO: eventually only include properties changed since the params.lastViewFrustumSent time -EntityPropertyFlags AnchorEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); - return requestedProperties; -} - -void AnchorEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; -} - diff --git a/libraries/entities/src/AnchorEntityItem.h b/libraries/entities/src/AnchorEntityItem.h deleted file mode 100644 index bd385517c3..0000000000 --- a/libraries/entities/src/AnchorEntityItem.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// AnchorEntityItem.h -// libraries/entities/src -// -// Created by Thijs Wenker on 11/4/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 -// - -#ifndef hifi_AnchorEntityItem_h -#define hifi_AnchorEntityItem_h - -#include "EntityItem.h" - -class AnchorEntityItem : public EntityItem { -public: - static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); - - AnchorEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties); - - ALLOW_INSTANTIATION // This class can be instantiated - - // methods for getting/setting all properties of an entity - virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; - virtual bool setProperties(const EntityItemProperties& properties); - - // TODO: eventually only include properties changed since the params.lastViewFrustumSent time - virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const; - - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged); - -}; - -#endif // hifi_AnchorEntityItem_h diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index f5915ae8c3..50b98d4b5e 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -29,7 +29,6 @@ #include "LineEntityItem.h" #include "PolyVoxEntityItem.h" #include "PolyLineEntityItem.h" -#include "AnchorEntityItem.h" QMap EntityTypes::_typeToNameMap; QMap EntityTypes::_nameToTypeMap; @@ -50,7 +49,6 @@ REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(PolyLine) -REGISTER_ENTITY_TYPE(Anchor); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index dbe9569f63..30b6edbc07 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -48,8 +48,7 @@ public: Line, PolyVox, PolyLine, - Anchor, - LAST = Anchor + LAST = PolyLine } EntityType; static const QString& getEntityTypeName(EntityType entityType); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 64936c3a59..24034ff9b3 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_ANCHOR; + return VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING; case PacketType::AvatarData: case PacketType::BulkAvatarData: default: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 56cb60e02b..82d905bf28 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -146,6 +146,5 @@ 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; const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49; -const PacketVersion VERSION_ENTITIES_ANCHOR = 50; #endif // hifi_PacketHeaders_h From 764af63ea97aec235d8aa3e265fa2aaa616b6107 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 12 Nov 2015 02:19:08 +0100 Subject: [PATCH 0878/1003] remove Anchor entity from example and edit script, replace with invisible Box in example --- examples/edit.js | 23 ------------------- examples/example/securityCamera.js | 3 ++- examples/libraries/entityPropertyDialogBox.js | 6 ++--- libraries/entities/src/EntityItemProperties.h | 1 - libraries/entities/src/EntityTypes.cpp | 2 +- 5 files changed, 5 insertions(+), 30 deletions(-) diff --git a/examples/edit.js b/examples/edit.js index cf5163b824..74551384c9 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -155,7 +155,6 @@ var toolBar = (function() { newWebButton, newZoneButton, newPolyVoxButton, - newAnchorButton, browseMarketplaceButton; function initialize() { @@ -300,20 +299,6 @@ var toolBar = (function() { visible: false }); - newAnchorButton = toolBar.addTool({ - imageURL: toolIconUrl + "add-anchor.svg", - subImage: { - x: 0, - y: Tool.IMAGE_WIDTH, - width: Tool.IMAGE_WIDTH, - height: Tool.IMAGE_HEIGHT - }, - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: false - }); - that.setActive(false); } @@ -360,7 +345,6 @@ var toolBar = (function() { toolBar.showTool(newWebButton, doShow); toolBar.showTool(newZoneButton, doShow); toolBar.showTool(newPolyVoxButton, doShow); - toolBar.showTool(newAnchorButton, doShow); }; var RESIZE_INTERVAL = 50; @@ -617,13 +601,6 @@ var toolBar = (function() { } - return true; - } - if (newAnchorButton === toolBar.clicked(clickedOverlay)) { - createNewEntity({ - type: "Anchor" - }); - return true; } diff --git a/examples/example/securityCamera.js b/examples/example/securityCamera.js index 15d2c03e2e..6f5ca549cd 100644 --- a/examples/example/securityCamera.js +++ b/examples/example/securityCamera.js @@ -28,7 +28,8 @@ var cameraLookAt = function(cameraPos, lookAtPos) { }; cameraEntity = Entities.addEntity({ - type: "Anchor", + type: "Box", + visible: false, position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET) }); diff --git a/examples/libraries/entityPropertyDialogBox.js b/examples/libraries/entityPropertyDialogBox.js index b34bec2a25..39ffbed629 100644 --- a/examples/libraries/entityPropertyDialogBox.js +++ b/examples/libraries/entityPropertyDialogBox.js @@ -261,10 +261,8 @@ EntityPropertyDialogBox = (function () { index++; } - if (properties.type == "Camera") { - array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); - index++; - } + array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" }); + index++; array.push({ button: "Cancel" }); index++; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 1f08789cdc..84a5aeca5d 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,7 +64,6 @@ class EntityItemProperties { friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods friend class PolyLineEntityItem; // TODO: consider removing this friend relationship and use public methods - friend class AnchorEntityItem; // TODO: consider removing this friend relationship and use public methods public: EntityItemProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()); virtual ~EntityItemProperties() = default; diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 50b98d4b5e..52c2242629 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -48,7 +48,7 @@ REGISTER_ENTITY_TYPE(ParticleEffect) REGISTER_ENTITY_TYPE(Zone) REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyVox) -REGISTER_ENTITY_TYPE(PolyLine) +REGISTER_ENTITY_TYPE(PolyLine); const QString& EntityTypes::getEntityTypeName(EntityType entityType) { QMap::iterator matchedTypeName = _typeToNameMap.find(entityType); From 118d05d824935f6dfd067c41e2b31667fe6b0227 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Nov 2015 17:24:50 -0800 Subject: [PATCH 0879/1003] Use a clearer function --- libraries/shared/src/GLMHelpers.cpp | 2 +- libraries/shared/src/GLMHelpers.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index b57754066b..fa010a85bd 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -201,7 +201,7 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2) { // Helper function return the rotation from the first vector onto the second glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { - return glm::quat(glm::normalize(v1), glm::normalize(v2)); + return glm::rotation(glm::normalize(v1), glm::normalize(v2)); } bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) { diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 9c1bbe23a4..8d3410aaf2 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -16,6 +16,7 @@ #include #include +#include // Bring the most commonly used GLM types into the default namespace using glm::ivec2; From 954391ae3e832090f1ac4643482f759d08ad4473 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 11 Nov 2015 18:33:26 -0800 Subject: [PATCH 0880/1003] Fix models resizing When models are MUCH bigger one one axis than the others and we modify the scale only of one of those small axis, it wouldn't propagate because the change was relatively too small conpared to the size of the model --- libraries/render-utils/src/Model.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5b9bfdca3d..e201893209 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -89,11 +89,7 @@ void Model::setScale(const glm::vec3& scale) { } void Model::setScaleInternal(const glm::vec3& scale) { - float scaleLength = glm::length(_scale); - float relativeDeltaScale = glm::length(_scale - scale) / scaleLength; - - const float ONE_PERCENT = 0.01f; - if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { + if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) { _scale = scale; initJointTransforms(); } From 83d5c9cf4a367e871dd3445033e23ac15ee32133 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 Nov 2015 18:54:07 -0800 Subject: [PATCH 0881/1003] arrange status icons in two rows of 4 each --- libraries/render/src/render/DrawStatus.cpp | 1 + .../render/src/render/drawItemStatus.slv | 32 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index 3171b8160c..c7a54b18d0 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -163,6 +163,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex Transform viewMat; args->_viewFrustum->evalProjectionMatrix(projMat); args->_viewFrustum->evalViewTransform(viewMat); + batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv index f078e3fd65..cb4ae7ebd2 100644 --- a/libraries/render/src/render/drawItemStatus.slv +++ b/libraries/render/src/render/drawItemStatus.slv @@ -46,12 +46,14 @@ vec3 paintRainbow(float normalizedHue) { } const int INVALID_STATUS = int(0xFFFFFFFF); +const int MAX_NUM_ICONS = 8; +const int ICONS_PER_ROW = 4; int getIconStatus(int icon) { - if (icon < 4) { + if (icon < ICONS_PER_ROW) { return inStatus0[icon]; - } else if (icon < 8) { - return inStatus1[icon - 4]; + } else if (icon < MAX_NUM_ICONS) { + return inStatus1[icon - ICONS_PER_ROW]; } return INVALID_STATUS; } @@ -63,10 +65,8 @@ vec3 unpackStatus(int v) { } void main(void) { - const vec2 ICON_PIXEL_SIZE = vec2(20, 20); - const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); - const int NUM_VERTICES = 6; - const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES]( + const int NUM_VERTICES_PER_ICON = 6; + const vec4 UNIT_QUAD[NUM_VERTICES_PER_ICON] = vec4[NUM_VERTICES_PER_ICON]( vec4(-1.0, -1.0, 0.0, 1.0), vec4(1.0, -1.0, 0.0, 1.0), vec4(-1.0, 1.0, 0.0, 1.0), @@ -75,6 +75,17 @@ void main(void) { vec4(1.0, 1.0, 0.0, 1.0) ); + const vec2 ICON_PIXEL_SIZE = vec2(20, 20); + const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); + const vec2 ICON_GRID_SLOTS[MAX_NUM_ICONS] = vec2[MAX_NUM_ICONS](vec2(-1.5, 0.5), + vec2(-0.5, 0.5), + vec2(0.5, 0.5), + vec2(1.5, 0.5), + vec2(-1.5,-0.5), + vec2(-0.5,-0.5), + vec2(0.5, -0.5), + vec2(1.5, -0.5)); + // anchor point in clip space vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0); TransformCamera cam = getTransformCamera(); @@ -82,7 +93,7 @@ void main(void) { <$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$> // Which icon are we dealing with ? - int iconNum = gl_VertexID / NUM_VERTICES; + int iconNum = gl_VertexID / NUM_VERTICES_PER_ICON; int packedIconStatus = getIconStatus(iconNum); // if invalid, just kill @@ -93,7 +104,7 @@ void main(void) { } // Which quad vertex pos? - int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; + int twoTriID = gl_VertexID - iconNum * NUM_VERTICES_PER_ICON; vec4 quadPos = UNIT_QUAD[twoTriID]; // unpack to get x and y satus @@ -110,7 +121,8 @@ void main(void) { iconScale = max(vec2(0, 0), (iconScale * iconStatus.x)); //Offset icon to the right based on the iconNum - vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); + vec2 gridOffset = ICON_GRID_SLOTS[iconNum]; + vec2 offset = gridOffset * (ICON_PIXEL_SIZE + MARGIN_PIXEL_SIZE); // Final position in pixel space vec2 quadPixelPos = offset.xy + quadPos.xy * 0.5 * iconScale; From 33851baa3f953c44a4a58c82a406e887fb34031d Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 11 Nov 2015 22:32:42 -0800 Subject: [PATCH 0882/1003] Add directionality to examples/growth *There is a bug in here, but it may be in Interface.* The sphere entities have a lifetime of 60s. The first time the script is run, each click fires one clickReleaseOnEntity event. If the entities expire (lifetime is reached), and the script is rerun a second time, each click will fire two clickReleaseOnEntity events. If this is repeated (waiting for expiration each time), then the N-th repetition will fire N clickReleaseOnEntity events. Note that each entity, across runs, has a unique uuid, as expected. --- examples/growth.js | 69 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/examples/growth.js b/examples/growth.js index 8a730da6c1..8bd49d92c5 100644 --- a/examples/growth.js +++ b/examples/growth.js @@ -8,40 +8,87 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // var RADIUS = 5; -var LIFETIME = 6000; +var LIFETIME = 60; // only keep these entities around for a minute + +var SIZE = 1; // starting size +var SIZE_MAX = 3; +var SIZE_MIN = 0.5; -var size = 1; var center = MyAvatar.position; var positions = [ Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.FRONT)), Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.FRONT)), Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.RIGHT)), Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.RIGHT)), - Vec3.sum(center, Vec3.multiply(RADIUS, Vec3.UP)), - Vec3.sum(center, Vec3.multiply(-RADIUS, Vec3.UP)), ]; +// Create spheres around the avatar var spheres = map(positions, getSphere); -Script.update.connect(function(delta) { - size += delta; // grow by 1 unit/s - each(spheres, function(sphere) { - Entities.editEntity(sphere, { dimensions: getDimensions(size) }); - }); +// Toggle sphere growth +each(spheres, function(sphere) { + var state = { + size: SIZE, + grow: true, // grow/shrink + state: false, // mutate/pause + }; + + // Grow or shrink on click + Script.addEventHandler(sphere, 'clickReleaseOnEntity', toggleGrowth); + // TODO: Toggle on collisions as well + + function toggleGrowth() { + if (state.state) { + stop(); + } else { + start(); + } + } + + function start() { + var verb = state.grow ? 'grow' : 'shrink'; + print('starting to '+verb+' '+sphere); + + Script.update.connect(grow); + state.state = true; + } + + function stop() { + print('stopping '+sphere); + + Script.update.disconnect(grow); + state.state = false; + state.grow = !state.grow; + } + + function grow(delta) { + if ((state.grow && state.size > SIZE_MAX) || // cannot grow more + (!state.grow && state.size < SIZE_MIN)) { // cannot shrink more + stop(); + return; + } + + state.size += (state.grow ? 1 : -1) * delta; // grow/shrink + Entities.editEntity(sphere, { dimensions: getDimensions(state.size) }); + } }); -Script.scriptEnding.connect(function() { + +Script.scriptEnding.connect(function() { // cleanup each(spheres, function(sphere) { Entities.deleteEntity(sphere); }); }); +// NOTE: Everything below this line is a helper. +// The basic logic of this example is entirely above this note. + // Entity helpers function getSphere(position) { return Entities.addEntity({ type: 'Sphere', position: position, - dimensions: getDimensions(size), + dimensions: getDimensions(SIZE), color: getColor(), gravity: Vec3.ZERO, lifetime: LIFETIME, From f521be10fedd734003254e9beb38984884e95498 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 10 Nov 2015 10:19:53 -0800 Subject: [PATCH 0883/1003] Avatar recording work in progress --- assignment-client/CMakeLists.txt | 2 +- interface/src/avatar/MyAvatar.cpp | 88 +++--- interface/src/avatar/MyAvatar.h | 10 +- libraries/avatars/CMakeLists.txt | 2 +- libraries/avatars/src/AvatarData.cpp | 272 ++++++++++++++---- libraries/avatars/src/AvatarData.h | 29 +- libraries/avatars/src/HeadData.cpp | 14 +- libraries/avatars/src/HeadData.h | 2 + libraries/avatars/src/Player.cpp | 3 + libraries/avatars/src/Player.h | 4 + libraries/avatars/src/Recorder.cpp | 2 + libraries/avatars/src/Recorder.h | 5 +- libraries/avatars/src/Recording.cpp | 2 + libraries/avatars/src/Recording.h | 5 +- libraries/recording/src/recording/Clip.cpp | 2 +- libraries/recording/src/recording/Clip.h | 6 +- libraries/recording/src/recording/Deck.cpp | 115 +++++++- libraries/recording/src/recording/Deck.h | 52 +++- libraries/recording/src/recording/Forward.h | 7 + libraries/recording/src/recording/Frame.cpp | 16 +- libraries/recording/src/recording/Frame.h | 3 +- .../recording/src/recording/Recorder.cpp | 18 +- libraries/recording/src/recording/Recorder.h | 8 +- .../src/recording/impl/BufferClip.cpp | 14 +- .../recording/src/recording/impl/BufferClip.h | 6 +- .../recording/src/recording/impl/FileClip.cpp | 32 ++- .../recording/src/recording/impl/FileClip.h | 8 +- libraries/script-engine/CMakeLists.txt | 2 +- libraries/shared/src/shared/JSONHelpers.cpp | 57 ++++ libraries/shared/src/shared/JSONHelpers.h | 23 ++ .../shared/src/shared/UniformTransform.cpp | 84 ++++++ .../shared/src/shared/UniformTransform.h | 40 +++ 32 files changed, 777 insertions(+), 156 deletions(-) create mode 100644 libraries/shared/src/shared/JSONHelpers.cpp create mode 100644 libraries/shared/src/shared/JSONHelpers.h create mode 100644 libraries/shared/src/shared/UniformTransform.cpp create mode 100644 libraries/shared/src/shared/UniformTransform.h diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 5b92dfba99..830164eb60 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -5,7 +5,7 @@ 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 + networking animation recording shared script-engine embedded-webserver controllers physics ) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6f60ad179c..852e1d1389 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -35,7 +35,10 @@ #include #include #include - +#include +#include +#include +#include #include "devices/Faceshift.h" @@ -77,6 +80,10 @@ const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amaz const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; +static const QString HEADER_NAME = "com.highfidelity.recording.AvatarData"; +static recording::FrameType AVATAR_FRAME_TYPE = recording::Frame::TYPE_INVALID; +static std::once_flag frameTypeRegistration; + MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), @@ -112,6 +119,19 @@ MyAvatar::MyAvatar(RigPointer rig) : _audioListenerMode(FROM_HEAD), _hmdAtRestDetector(glm::vec3(0), glm::quat()) { + using namespace recording; + + std::call_once(frameTypeRegistration, [] { + AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(HEADER_NAME); + }); + + // FIXME how to deal with driving multiple avatars locally? + Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::Pointer frame) { + qDebug() << "Playback of avatar frame length: " << frame->data.size(); + avatarStateFromFrame(frame->data, this); + }); + + for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } @@ -235,14 +255,12 @@ void MyAvatar::update(float deltaTime) { simulate(deltaTime); } +extern QByteArray avatarStateToFrame(const AvatarData* _avatar); +extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); + void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); - // Play back recording - if (_player && _player->isPlaying()) { - _player->play(); - } - if (_scale != _targetScale) { float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; setScale(scale); @@ -310,7 +328,7 @@ void MyAvatar::simulate(float deltaTime) { // Record avatars movements. if (_recorder && _recorder->isRecording()) { - _recorder->record(); + _recorder->recordFrame(AVATAR_FRAME_TYPE, avatarStateToFrame(this)); } // consider updating our billboard @@ -580,33 +598,35 @@ bool MyAvatar::isRecording() { return _recorder && _recorder->isRecording(); } -qint64 MyAvatar::recorderElapsed() { +float MyAvatar::recorderElapsed() { + if (QThread::currentThread() != thread()) { + float result; + QMetaObject::invokeMethod(this, "recorderElapsed", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(float, result)); + return result; + } if (!_recorder) { return 0; } - if (QThread::currentThread() != thread()) { - qint64 result; - QMetaObject::invokeMethod(this, "recorderElapsed", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(qint64, result)); - return result; - } - return _recorder->elapsed(); + return (float)_recorder->position() / MSECS_PER_SECOND; } +QMetaObject::Connection _audioClientRecorderConnection; + void MyAvatar::startRecording() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection); return; } - if (!_recorder) { - _recorder = QSharedPointer::create(this); - } + + _recorder = std::make_shared(); // connect to AudioClient's signal so we get input audio auto audioClient = DependencyManager::get(); - connect(audioClient.data(), &AudioClient::inputReceived, _recorder.data(), - &Recorder::recordAudio, Qt::QueuedConnection); - - _recorder->startRecording(); + _audioClientRecorderConnection = connect(audioClient.data(), &AudioClient::inputReceived, [] { + // FIXME, missing audio data handling + }); + setRecordingBasis(); + _recorder->start(); } void MyAvatar::stopRecording() { @@ -618,15 +638,14 @@ void MyAvatar::stopRecording() { return; } if (_recorder) { - // stop grabbing audio from the AudioClient - auto audioClient = DependencyManager::get(); - disconnect(audioClient.data(), 0, _recorder.data(), 0); - - _recorder->stopRecording(); + QObject::disconnect(_audioClientRecorderConnection); + _audioClientRecorderConnection = QMetaObject::Connection(); + _recorder->stop(); + clearRecordingBasis(); } } -void MyAvatar::saveRecording(QString filename) { +void MyAvatar::saveRecording(const QString& filename) { if (!_recorder) { qCDebug(interfaceapp) << "There is no recording to save"; return; @@ -636,8 +655,10 @@ void MyAvatar::saveRecording(QString filename) { Q_ARG(QString, filename)); return; } + if (_recorder) { - _recorder->saveToFile(filename); + auto clip = _recorder->getClip(); + recording::Clip::toFile(filename, clip); } } @@ -646,15 +667,18 @@ void MyAvatar::loadLastRecording() { QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection); return; } - if (!_recorder) { + + if (!_recorder || !_recorder->getClip()) { qCDebug(interfaceapp) << "There is no recording to load"; return; } + if (!_player) { - _player = QSharedPointer::create(this); + _player = std::make_shared(); } - _player->loadRecording(_recorder->getRecording()); + _player->queueClip(_recorder->getClip()); + _player->play(); } void MyAvatar::startAnimation(const QString& url, float fps, float priority, diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index da836b7f15..52f1ffce3f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -257,10 +257,10 @@ public slots: bool setJointReferential(const QUuid& id, int jointIndex); bool isRecording(); - qint64 recorderElapsed(); + float recorderElapsed(); void startRecording(); void stopRecording(); - void saveRecording(QString filename); + void saveRecording(const QString& filename); void loadLastRecording(); virtual void rebuildSkeletonBody() override; @@ -311,8 +311,8 @@ private: const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, bool allowDuplicates = false, bool useSaved = true) override; - const RecorderPointer getRecorder() const { return _recorder; } - const PlayerPointer getPlayer() const { return _player; } + const recording::RecorderPointer getRecorder() const { return _recorder; } + const recording::DeckPointer getPlayer() const { return _player; } //void beginFollowingHMD(); //bool shouldFollowHMD() const; @@ -360,7 +360,7 @@ private: eyeContactTarget _eyeContactTarget; - RecorderPointer _recorder; + recording::RecorderPointer _recorder; glm::vec3 _trackedHeadPosition; diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index 849828bbf6..6d4d9cc341 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME avatars) setup_hifi_library(Network Script) -link_hifi_libraries(audio shared networking) +link_hifi_libraries(audio shared networking recording) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a698c6f374..c4e356d402 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include @@ -25,6 +28,10 @@ #include #include #include +#include +#include +#include +#include #include "AvatarLogging.h" #include "AvatarData.h" @@ -62,7 +69,6 @@ AvatarData::AvatarData() : _targetVelocity(0.0f), _localAABox(DEFAULT_LOCAL_AABOX_CORNER, DEFAULT_LOCAL_AABOX_SCALE) { - } AvatarData::~AvatarData() { @@ -791,7 +797,7 @@ bool AvatarData::isPaused() { return _player && _player->isPaused(); } -qint64 AvatarData::playerElapsed() { +float AvatarData::playerElapsed() { if (!_player) { return 0; } @@ -801,10 +807,10 @@ qint64 AvatarData::playerElapsed() { Q_RETURN_ARG(qint64, result)); return result; } - return _player->elapsed(); + return (float)_player->position() / MSECS_PER_SECOND; } -qint64 AvatarData::playerLength() { +float AvatarData::playerLength() { if (!_player) { return 0; } @@ -814,28 +820,24 @@ qint64 AvatarData::playerLength() { Q_RETURN_ARG(qint64, result)); return result; } - return _player->getRecording()->getLength(); + return _player->length() / MSECS_PER_SECOND; } -int AvatarData::playerCurrentFrame() { - return (_player) ? _player->getCurrentFrame() : 0; -} - -int AvatarData::playerFrameNumber() { - return (_player && _player->getRecording()) ? _player->getRecording()->getFrameNumber() : 0; -} - -void AvatarData::loadRecording(QString filename) { +void AvatarData::loadRecording(const QString& filename) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection, Q_ARG(QString, filename)); return; } - if (!_player) { - _player = QSharedPointer::create(this); + using namespace recording; + + ClipPointer clip = Clip::fromFile(filename); + if (!clip) { + qWarning() << "Unable to load clip data from " << filename; } - _player->loadFromFile(filename); + _player = std::make_shared(); + _player->queueClip(clip); } void AvatarData::startPlaying() { @@ -843,70 +845,56 @@ void AvatarData::startPlaying() { QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); return; } + if (!_player) { - _player = QSharedPointer::create(this); + qWarning() << "No clip loaded for playback"; + return; } - _player->startPlaying(); + setRecordingBasis(); + _player->play(); } void AvatarData::setPlayerVolume(float volume) { - if (_player) { - _player->setVolume(volume); - } + // FIXME } -void AvatarData::setPlayerAudioOffset(int audioOffset) { - if (_player) { - _player->setAudioOffset(audioOffset); - } +void AvatarData::setPlayerAudioOffset(float audioOffset) { + // FIXME } -void AvatarData::setPlayerFrame(unsigned int frame) { - if (_player) { - _player->setCurrentFrame(frame); - } -} +void AvatarData::setPlayerTime(float time) { + if (!_player) { + qWarning() << "No player active"; + return; + } -void AvatarData::setPlayerTime(unsigned int time) { - if (_player) { - _player->setCurrentTime(time); - } + _player->seek(time * MSECS_PER_SECOND); } void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) { - if (_player) { - _player->setPlayFromCurrentLocation(playFromCurrentLocation); - } + // FIXME } void AvatarData::setPlayerLoop(bool loop) { if (_player) { - _player->setLoop(loop); + _player->loop(loop); } } void AvatarData::setPlayerUseDisplayName(bool useDisplayName) { - if(_player) { - _player->useDisplayName(useDisplayName); - } + // FIXME } void AvatarData::setPlayerUseAttachments(bool useAttachments) { - if(_player) { - _player->useAttachements(useAttachments); - } + // FIXME } void AvatarData::setPlayerUseHeadModel(bool useHeadModel) { - if(_player) { - _player->useHeadModel(useHeadModel); - } + // FIXME } void AvatarData::setPlayerUseSkeletonModel(bool useSkeletonModel) { - if(_player) { - _player->useSkeletonModel(useSkeletonModel); - } + // FIXME } void AvatarData::play() { @@ -920,6 +908,10 @@ void AvatarData::play() { } } +std::shared_ptr AvatarData::getRecordingBasis() const { + return _recordingBasis; +} + void AvatarData::pausePlayer() { if (!_player) { return; @@ -929,7 +921,7 @@ void AvatarData::pausePlayer() { return; } if (_player) { - _player->pausePlayer(); + _player->pause(); } } @@ -942,7 +934,7 @@ void AvatarData::stopPlaying() { return; } if (_player) { - _player->stopPlaying(); + _player->stop(); } } @@ -1514,3 +1506,177 @@ void registerAvatarTypes(QScriptEngine* engine) { new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); } +void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { + if (!recordingBasis) { + recordingBasis = std::make_shared(); + recordingBasis->rotation = getOrientation(); + recordingBasis->translation = getPosition(); + recordingBasis->scale = getTargetScale(); + } + _recordingBasis = recordingBasis; +} + +void AvatarData::clearRecordingBasis() { + _recordingBasis.reset(); +} + +static const QString JSON_AVATAR_BASIS = QStringLiteral("basisTransform"); +static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform"); +static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations"); +static const QString JSON_AVATAR_HEAD = QStringLiteral("head"); +static const QString JSON_AVATAR_HEAD_ROTATION = QStringLiteral("rotation"); +static const QString JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS = QStringLiteral("blendShapes"); +static const QString JSON_AVATAR_HEAD_LEAN_FORWARD = QStringLiteral("leanForward"); +static const QString JSON_AVATAR_HEAD_LEAN_SIDEWAYS = QStringLiteral("leanSideways"); +static const QString JSON_AVATAR_HEAD_LOOKAT = QStringLiteral("lookAt"); +static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); +static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); +static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); +static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments"); + + +// Every frame will store both a basis for the recording and a relative transform +// This allows the application to decide whether playback should be relative to an avatar's +// transform at the start of playback, or relative to the transform of the recorded +// avatar +QByteArray avatarStateToFrame(const AvatarData* _avatar) { + QJsonObject root; + + if (!_avatar->getFaceModelURL().isEmpty()) { + root[JSON_AVATAR_HEAD_MODEL] = _avatar->getFaceModelURL().toString(); + } + if (!_avatar->getSkeletonModelURL().isEmpty()) { + root[JSON_AVATAR_BODY_MODEL] = _avatar->getSkeletonModelURL().toString(); + } + if (!_avatar->getDisplayName().isEmpty()) { + root[JSON_AVATAR_DISPLAY_NAME] = _avatar->getDisplayName(); + } + if (!_avatar->getAttachmentData().isEmpty()) { + // FIXME serialize attachment data + } + + auto recordingBasis = _avatar->getRecordingBasis(); + if (recordingBasis) { + // FIXME if the resulting relative basis is identity, we shouldn't record anything + // Record the transformation basis + root[JSON_AVATAR_BASIS] = recordingBasis->toJson(); + + // Record the relative transform + auto relativeTransform = recordingBasis->relativeTransform( + UniformTransform(_avatar->getPosition(), _avatar->getOrientation(), _avatar->getTargetScale())); + + root[JSON_AVATAR_RELATIVE] = relativeTransform.toJson(); + } + + QJsonArray jointRotations; + for (const auto& jointRotation : _avatar->getJointRotations()) { + jointRotations.push_back(toJsonValue(jointRotation)); + } + root[JSON_AVATAR_JOINT_ROTATIONS] = jointRotations; + + const HeadData* head = _avatar->getHeadData(); + if (head) { + QJsonObject headJson; + QJsonArray blendshapeCoefficients; + for (const auto& blendshapeCoefficient : head->getBlendshapeCoefficients()) { + blendshapeCoefficients.push_back(blendshapeCoefficient); + } + headJson[JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS] = blendshapeCoefficients; + headJson[JSON_AVATAR_HEAD_ROTATION] = toJsonValue(head->getRawOrientation()); + headJson[JSON_AVATAR_HEAD_LEAN_FORWARD] = QJsonValue(head->getLeanForward()); + headJson[JSON_AVATAR_HEAD_LEAN_SIDEWAYS] = QJsonValue(head->getLeanSideways()); + vec3 relativeLookAt = glm::inverse(_avatar->getOrientation()) * + (head->getLookAtPosition() - _avatar->getPosition()); + headJson[JSON_AVATAR_HEAD_LOOKAT] = toJsonValue(relativeLookAt); + root[JSON_AVATAR_HEAD] = headJson; + } + + return QJsonDocument(root).toBinaryData(); +} + +void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { + QJsonDocument doc = QJsonDocument::fromBinaryData(frameData); + QJsonObject root = doc.object(); + + if (root.contains(JSON_AVATAR_HEAD_MODEL)) { + auto faceModelURL = root[JSON_AVATAR_HEAD_MODEL].toString(); + if (faceModelURL != _avatar->getFaceModelURL().toString()) { + _avatar->setFaceModelURL(faceModelURL); + } + } + if (root.contains(JSON_AVATAR_BODY_MODEL)) { + auto bodyModelURL = root[JSON_AVATAR_BODY_MODEL].toString(); + if (bodyModelURL != _avatar->getSkeletonModelURL().toString()) { + _avatar->setSkeletonModelURL(bodyModelURL); + } + } + if (root.contains(JSON_AVATAR_DISPLAY_NAME)) { + auto newDisplayName = root[JSON_AVATAR_DISPLAY_NAME].toString(); + if (newDisplayName != _avatar->getDisplayName()) { + _avatar->setDisplayName(newDisplayName); + } + } + + // During playback you can either have the recording basis set to the avatar current state + // meaning that all playback is relative to this avatars starting position, or + // the basis can be loaded from the recording, meaning the playback is relative to the + // original avatar location + // The first is more useful for playing back recordings on your own avatar, while + // the latter is more useful for playing back other avatars within your scene. + auto currentBasis = _avatar->getRecordingBasis(); + if (!currentBasis) { + currentBasis = UniformTransform::parseJson(root[JSON_AVATAR_BASIS]); + } + + auto relativeTransform = UniformTransform::parseJson(root[JSON_AVATAR_RELATIVE]); + auto worldTransform = currentBasis->worldTransform(*relativeTransform); + _avatar->setPosition(worldTransform.translation); + _avatar->setOrientation(worldTransform.rotation); + _avatar->setTargetScale(worldTransform.scale); + +#if 0 + if (root.contains(JSON_AVATAR_ATTACHEMENTS)) { + // FIXME de-serialize attachment data + } + + // Joint rotations are relative to the avatar, so they require no basis correction + if (root.contains(JSON_AVATAR_JOINT_ROTATIONS)) { + QVector jointRotations; + QJsonArray jointRotationsJson = root[JSON_AVATAR_JOINT_ROTATIONS].toArray(); + jointRotations.reserve(jointRotationsJson.size()); + for (const auto& jointRotationJson : jointRotationsJson) { + jointRotations.push_back(quatFromJsonValue(jointRotationJson)); + } + } + + // Most head data is relative to the avatar, and needs no basis correction, + // but the lookat vector does need correction + HeadData* head = _avatar->_headData; + if (head && root.contains(JSON_AVATAR_HEAD)) { + QJsonObject headJson = root[JSON_AVATAR_HEAD].toObject(); + if (headJson.contains(JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS)) { + QVector blendshapeCoefficients; + QJsonArray blendshapeCoefficientsJson = headJson[JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS].toArray(); + for (const auto& blendshapeCoefficient : blendshapeCoefficientsJson) { + blendshapeCoefficients.push_back((float)blendshapeCoefficient.toDouble()); + } + head->setBlendshapeCoefficients(blendshapeCoefficients); + } + if (headJson.contains(JSON_AVATAR_HEAD_ROTATION)) { + head->setOrientation(quatFromJsonValue(headJson[JSON_AVATAR_HEAD_ROTATION])); + } + if (headJson.contains(JSON_AVATAR_HEAD_LEAN_FORWARD)) { + head->setLeanForward((float)headJson[JSON_AVATAR_HEAD_LEAN_FORWARD].toDouble()); + } + if (headJson.contains(JSON_AVATAR_HEAD_LEAN_SIDEWAYS)) { + head->setLeanSideways((float)headJson[JSON_AVATAR_HEAD_LEAN_SIDEWAYS].toDouble()); + } + if (headJson.contains(JSON_AVATAR_HEAD_LOOKAT)) { + auto relativeLookAt = vec3FromJsonValue(headJson[JSON_AVATAR_HEAD_LOOKAT]); + if (glm::length2(relativeLookAt) > 0.01) { + head->setLookAtPosition((_avatar->getOrientation() * relativeLookAt) + _avatar->getPosition()); + } + } + } +#endif +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 9079f15f53..ebb6e1a78f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -134,6 +134,7 @@ class QDataStream; class AttachmentData; class JointData; +struct UniformTransform; class AvatarData : public QObject { Q_OBJECT @@ -332,6 +333,11 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } + void clearRecordingBasis(); + std::shared_ptr getRecordingBasis() const; + void setRecordingBasis(std::shared_ptr recordingBasis = std::shared_ptr()); + + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); @@ -344,17 +350,13 @@ public slots: bool isPlaying(); bool isPaused(); - qint64 playerElapsed(); - qint64 playerLength(); - int playerCurrentFrame(); - int playerFrameNumber(); - - void loadRecording(QString filename); + float playerElapsed(); + float playerLength(); + void loadRecording(const QString& filename); void startPlaying(); void setPlayerVolume(float volume); - void setPlayerAudioOffset(int audioOffset); - void setPlayerFrame(unsigned int frame); - void setPlayerTime(unsigned int time); + void setPlayerAudioOffset(float audioOffset); + void setPlayerTime(float time); void setPlayFromCurrentLocation(bool playFromCurrentLocation); void setPlayerLoop(bool loop); void setPlayerUseDisplayName(bool useDisplayName); @@ -364,7 +366,7 @@ public slots: void play(); void pausePlayer(); void stopPlaying(); - + protected: QUuid _sessionUUID; glm::vec3 _position = START_LOCATION; @@ -418,7 +420,7 @@ protected: QWeakPointer _owningAvatarMixer; - PlayerPointer _player; + recording::DeckPointer _player; /// Loads the joint indices, names from the FST file (if any) virtual void updateJointMappings(); @@ -432,8 +434,13 @@ protected: SimpleMovingAverage _averageBytesReceived; QMutex avatarLock; // Name is redundant, but it aids searches. + + // During recording, this holds the starting position, orientation & scale of the recorded avatar + // During playback, it holds the + std::shared_ptr _recordingBasis; private: + friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&); diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index e853a3c57e..e971b184c8 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -42,8 +42,20 @@ HeadData::HeadData(AvatarData* owningAvatar) : } +glm::quat HeadData::getRawOrientation() const { + return glm::quat(glm::radians(glm::vec3(_basePitch, _baseYaw, _baseRoll))); +} + +void HeadData::setRawOrientation(const glm::quat& q) { + auto euler = glm::eulerAngles(q); + _basePitch = euler.x; + _baseYaw = euler.y; + _baseRoll = euler.z; +} + + glm::quat HeadData::getOrientation() const { - return _owningAvatar->getOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, _baseYaw, _baseRoll))); + return _owningAvatar->getOrientation() * getRawOrientation(); } void HeadData::setOrientation(const glm::quat& orientation) { diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index e2c3f69c39..38503f6e1e 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -48,6 +48,8 @@ public: virtual float getFinalYaw() const { return _baseYaw; } virtual float getFinalPitch() const { return _basePitch; } virtual float getFinalRoll() const { return _baseRoll; } + virtual glm::quat getRawOrientation() const; + virtual void setRawOrientation(const glm::quat& orientation); glm::quat getOrientation() const; void setOrientation(const glm::quat& orientation); diff --git a/libraries/avatars/src/Player.cpp b/libraries/avatars/src/Player.cpp index 47fc1390d9..31efb4cd9c 100644 --- a/libraries/avatars/src/Player.cpp +++ b/libraries/avatars/src/Player.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#if 0 #include #include #include @@ -438,3 +440,4 @@ bool Player::computeCurrentFrame() { return true; } +#endif diff --git a/libraries/avatars/src/Player.h b/libraries/avatars/src/Player.h index 96f3cbc268..558ff309e6 100644 --- a/libraries/avatars/src/Player.h +++ b/libraries/avatars/src/Player.h @@ -12,6 +12,9 @@ #ifndef hifi_Player_h #define hifi_Player_h +#include + +#if 0 #include #include @@ -86,5 +89,6 @@ private: bool _useHeadURL; bool _useSkeletonURL; }; +#endif #endif // hifi_Player_h diff --git a/libraries/avatars/src/Recorder.cpp b/libraries/avatars/src/Recorder.cpp index 5e47c296eb..343302d472 100644 --- a/libraries/avatars/src/Recorder.cpp +++ b/libraries/avatars/src/Recorder.cpp @@ -10,6 +10,7 @@ // +#if 0 #include #include #include @@ -143,3 +144,4 @@ void Recorder::record() { void Recorder::recordAudio(const QByteArray& audioByteArray) { _recording->addAudioPacket(audioByteArray); } +#endif diff --git a/libraries/avatars/src/Recorder.h b/libraries/avatars/src/Recorder.h index f81539a417..15bffcec8b 100644 --- a/libraries/avatars/src/Recorder.h +++ b/libraries/avatars/src/Recorder.h @@ -12,6 +12,9 @@ #ifndef hifi_Recorder_h #define hifi_Recorder_h +#include + +#if 0 #include "Recording.h" template @@ -49,6 +52,6 @@ private: AvatarData* _avatar; }; - +#endif #endif // hifi_Recorder_h diff --git a/libraries/avatars/src/Recording.cpp b/libraries/avatars/src/Recording.cpp index 26c5ab66dd..884ed495be 100644 --- a/libraries/avatars/src/Recording.cpp +++ b/libraries/avatars/src/Recording.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#if 0 #include #include #include @@ -659,3 +660,4 @@ RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString return recording; } +#endif diff --git a/libraries/avatars/src/Recording.h b/libraries/avatars/src/Recording.h index 7657a12b46..a5829b1e2f 100644 --- a/libraries/avatars/src/Recording.h +++ b/libraries/avatars/src/Recording.h @@ -12,6 +12,8 @@ #ifndef hifi_Recording_h #define hifi_Recording_h +#if 0 + #include #include @@ -124,5 +126,6 @@ private: void writeRecordingToFile(RecordingPointer recording, const QString& filename); RecordingPointer readRecordingFromFile(RecordingPointer recording, const QString& filename); - +RecordingPointer readRecordingFromRecFile(RecordingPointer recording, const QString& filename, const QByteArray& byteArray); +#endif #endif // hifi_Recording_h diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index 09acf0579f..ba13b359f0 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -35,7 +35,7 @@ Clip::Pointer Clip::duplicate() { Clip::Pointer result = std::make_shared(); Locker lock(_mutex); - float currentPosition = position(); + Time currentPosition = position(); seek(0); Frame::Pointer frame = nextFrame(); diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index e7034ef077..70fc4b3f7f 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -28,11 +28,11 @@ public: Pointer duplicate(); - virtual float duration() const = 0; + virtual Time duration() const = 0; virtual size_t frameCount() const = 0; - virtual void seek(float offset) = 0; - virtual float position() const = 0; + virtual void seek(Time offset) = 0; + virtual Time position() const = 0; virtual FramePointer peekFrame() const = 0; virtual FramePointer nextFrame() = 0; diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index f0db37078b..4349a39732 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -6,7 +6,116 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Deck.h" + +#include +#include -// FIXME -- DO NOT include headers in empty CPP files, it produces warnings. Once we define new symbols -// and some actual code here, we can uncomment this include. -//#include "Deck.h" +#include "Clip.h" +#include "Frame.h" +#include "Logging.h" + +using namespace recording; + +void Deck::queueClip(ClipPointer clip, Time timeOffset) { + if (!clip) { + qCWarning(recordingLog) << "Clip invalid, ignoring"; + return; + } + + // FIXME if the time offset is not zero, wrap the clip in a OffsetClip wrapper + _clips.push_back(clip); +} + +void Deck::play() { + if (_pause) { + _pause = false; + _startEpoch = usecTimestampNow() - (_position * USECS_PER_MSEC); + emit playbackStateChanged(); + processFrames(); + } +} + +void Deck::pause() { + if (!_pause) { + _pause = true; + emit playbackStateChanged(); + } +} + +Clip::Pointer Deck::getNextClip() { + Clip::Pointer result; + Time soonestFramePosition = INVALID_TIME; + for (const auto& clip : _clips) { + Time nextFramePosition = clip->position(); + if (nextFramePosition < soonestFramePosition) { + result = clip; + soonestFramePosition = nextFramePosition; + } + } + return result; +} + +void Deck::seek(Time position) { + _position = position; + // FIXME reset the frames to the appropriate spot + for (auto& clip : _clips) { + clip->seek(position); + } + + if (!_pause) { + // FIXME what if the timer is already running? + processFrames(); + } +} + +Time Deck::position() const { + if (_pause) { + return _position; + } + return (usecTimestampNow() - _startEpoch) / USECS_PER_MSEC; +} + +static const Time MIN_FRAME_WAIT_INTERVAL_MS = 1; + +void Deck::processFrames() { + if (_pause) { + return; + } + + _position = position(); + auto triggerPosition = _position + MIN_FRAME_WAIT_INTERVAL_MS; + Clip::Pointer nextClip; + for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) { + // If the clip is too far in the future, just break out of the handling loop + Time framePosition = nextClip->position(); + if (framePosition > triggerPosition) { + break; + } + + // Handle the frame and advance the clip + Frame::handleFrame(nextClip->nextFrame()); + } + + + if (!nextClip) { + qCDebug(recordingLog) << "No more frames available"; + // No more frames available, so handle the end of playback + if (_loop) { + qCDebug(recordingLog) << "Looping enabled, seeking back to beginning"; + // If we have looping enabled, start the playback over + seek(0); + } else { + // otherwise pause playback + pause(); + } + return; + } + + // If we have more clip frames available, set the timer for the next one + Time nextClipPosition = nextClip->position(); + Time interval = nextClipPosition - _position; + _timer.singleShot(interval, [this] { + processFrames(); + }); +} diff --git a/libraries/recording/src/recording/Deck.h b/libraries/recording/src/recording/Deck.h index 2ae8d6a7be..a3b4405210 100644 --- a/libraries/recording/src/recording/Deck.h +++ b/libraries/recording/src/recording/Deck.h @@ -10,26 +10,62 @@ #ifndef hifi_Recording_Deck_h #define hifi_Recording_Deck_h -#include "Forward.h" +#include +#include #include +#include + +#include "Forward.h" -class QIODevice; namespace recording { class Deck : public QObject { + Q_OBJECT public: using Pointer = std::shared_ptr; - Deck(QObject* parent = nullptr) : QObject(parent) {} - virtual ~Deck(); // Place a clip on the deck for recording or playback - void queueClip(ClipPointer clip, float timeOffset = 0.0f); - void play(float timeOffset = 0.0f); - void reposition(float timeOffsetDelta); - void setPlaybackSpeed(float rate); + void queueClip(ClipPointer clip, Time timeOffset = 0.0f); + + void play(); + bool isPlaying() { return !_pause; } + + void pause(); + bool isPaused() const { return _pause; } + + void stop() { pause(); seek(0.0f); } + + Time length() const { return _length; } + + void loop(bool enable = true) { _loop = enable; } + bool isLooping() const { return _loop; } + + Time position() const; + void seek(Time position); + + void setPlaybackSpeed(float factor) { _playbackSpeed = factor; } + float getPlaybackSpeed() { return _playbackSpeed; } + +signals: + void playbackStateChanged(); + +private: + using Clips = std::list; + + ClipPointer getNextClip(); + void processFrames(); + + QTimer _timer; + Clips _clips; + quint64 _startEpoch { 0 }; + Time _position { 0 }; + float _playbackSpeed { 1.0f }; + bool _pause { true }; + bool _loop { false }; + Time _length { 0 }; }; } diff --git a/libraries/recording/src/recording/Forward.h b/libraries/recording/src/recording/Forward.h index 5bd6dd917f..31aa40521c 100644 --- a/libraries/recording/src/recording/Forward.h +++ b/libraries/recording/src/recording/Forward.h @@ -12,11 +12,18 @@ #include #include +#include namespace recording { +using Time = uint32_t; + +static const Time INVALID_TIME = std::numeric_limits::max(); + using FrameType = uint16_t; +using FrameSize = uint16_t; + struct Frame; using FramePointer = std::shared_ptr; diff --git a/libraries/recording/src/recording/Frame.cpp b/libraries/recording/src/recording/Frame.cpp index aac8a4d9c3..ad2a583424 100644 --- a/libraries/recording/src/recording/Frame.cpp +++ b/libraries/recording/src/recording/Frame.cpp @@ -82,7 +82,8 @@ FrameType Frame::registerFrameType(const QString& frameTypeName) { Q_ASSERT(headerType == Frame::TYPE_HEADER); Q_UNUSED(headerType); // FIXME - build system on unix still not upgraded to Qt 5.5.1 so Q_ASSERT still produces warnings }); - return frameTypes.registerValue(frameTypeName); + auto result = frameTypes.registerValue(frameTypeName); + return result; } QMap Frame::getFrameTypes() { @@ -102,3 +103,16 @@ Frame::Handler Frame::registerFrameHandler(FrameType type, Handler handler) { handlerMap[type] = handler; return result; } + +void Frame::handleFrame(const Frame::Pointer& frame) { + Handler handler; + { + Locker lock(mutex); + auto iterator = handlerMap.find(frame->type); + if (iterator == handlerMap.end()) { + return; + } + handler = *iterator; + } + handler(frame); +} diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index 0fb95c4b2e..563b042656 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -26,7 +26,7 @@ public: static const FrameType TYPE_INVALID = 0xFFFF; static const FrameType TYPE_HEADER = 0x0; FrameType type { TYPE_INVALID }; - float timeOffset { 0 }; + Time timeOffset { 0 }; QByteArray data; Frame() {} @@ -37,6 +37,7 @@ public: static QMap getFrameTypes(); static QMap getFrameTypeNames(); static Handler registerFrameHandler(FrameType type, Handler handler); + static void handleFrame(const Pointer& frame); }; } diff --git a/libraries/recording/src/recording/Recorder.cpp b/libraries/recording/src/recording/Recorder.cpp index b2e7399cd4..f007367cae 100644 --- a/libraries/recording/src/recording/Recorder.cpp +++ b/libraries/recording/src/recording/Recorder.cpp @@ -9,25 +9,35 @@ #include "Recorder.h" #include +#include #include "impl/BufferClip.h" #include "Frame.h" using namespace recording; +Recorder::~Recorder() { + +} + +Time Recorder::position() { + return 0.0f; +} + void Recorder::start() { if (!_recording) { _recording = true; if (!_clip) { _clip = std::make_shared(); } + _startEpoch = usecTimestampNow(); _timer.start(); emit recordingStateChanged(); } } void Recorder::stop() { - if (!_recording) { + if (_recording) { _recording = false; _elapsed = _timer.elapsed(); emit recordingStateChanged(); @@ -50,13 +60,11 @@ void Recorder::recordFrame(FrameType type, QByteArray frameData) { Frame::Pointer frame = std::make_shared(); frame->type = type; frame->data = frameData; - frame->timeOffset = (float)(_elapsed + _timer.elapsed()) / MSECS_PER_SECOND; + frame->timeOffset = (usecTimestampNow() - _startEpoch) / USECS_PER_MSEC; _clip->addFrame(frame); } ClipPointer Recorder::getClip() { - auto result = _clip; - _clip.reset(); - return result; + return _clip; } diff --git a/libraries/recording/src/recording/Recorder.h b/libraries/recording/src/recording/Recorder.h index deae543bb0..f8346456d4 100644 --- a/libraries/recording/src/recording/Recorder.h +++ b/libraries/recording/src/recording/Recorder.h @@ -20,18 +20,23 @@ namespace recording { // An interface for interacting with clips, creating them by recording or // playing them back. Also serialization to and from files / network sources class Recorder : public QObject { + Q_OBJECT public: using Pointer = std::shared_ptr; Recorder(QObject* parent = nullptr) : QObject(parent) {} virtual ~Recorder(); + Time position(); + // Start recording frames void start(); // Stop recording void stop(); + // Test if recording is active bool isRecording(); + // Erase the currently recorded content void clear(); @@ -46,7 +51,8 @@ signals: private: QElapsedTimer _timer; ClipPointer _clip; - quint64 _elapsed; + quint64 _elapsed { 0 }; + quint64 _startEpoch { 0 }; bool _recording { false }; }; diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index 4d5a910d42..5dc75bbce2 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -8,24 +8,26 @@ #include "BufferClip.h" +#include + #include "../Frame.h" using namespace recording; -void BufferClip::seek(float offset) { +void BufferClip::seek(Time offset) { Locker lock(_mutex); - auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, - [](Frame::Pointer a, float b)->bool{ + auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, + [](Frame::Pointer a, Time b)->bool { return a->timeOffset < b; } ); _frameIndex = itr - _frames.begin(); } -float BufferClip::position() const { +Time BufferClip::position() const { Locker lock(_mutex); - float result = std::numeric_limits::max(); + Time result = INVALID_TIME; if (_frameIndex < _frames.size()) { result = _frames[_frameIndex]->timeOffset; } @@ -77,7 +79,7 @@ void BufferClip::reset() { _frameIndex = 0; } -float BufferClip::duration() const { +Time BufferClip::duration() const { if (_frames.empty()) { return 0; } diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index b40687a4ec..bfb0234600 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -22,11 +22,11 @@ public: virtual ~BufferClip() {} - virtual float duration() const override; + virtual Time duration() const override; virtual size_t frameCount() const override; - virtual void seek(float offset) override; - virtual float position() const override; + virtual void seek(Time offset) override; + virtual Time position() const override; virtual FramePointer peekFrame() const override; virtual FramePointer nextFrame() override; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index be7230e3f8..e64085517a 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -22,7 +22,7 @@ using namespace recording; -static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(float) + sizeof(uint16_t); +static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(Time) + sizeof(FrameSize); static const QString FRAME_TYPE_MAP = QStringLiteral("frameTypes"); @@ -60,10 +60,10 @@ FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) { FrameHeader header; memcpy(&(header.type), current, sizeof(FrameType)); current += sizeof(FrameType); - memcpy(&(header.timeOffset), current, sizeof(float)); - current += sizeof(float); - memcpy(&(header.size), current, sizeof(uint16_t)); - current += sizeof(uint16_t); + memcpy(&(header.timeOffset), current, sizeof(Time)); + current += sizeof(Time); + memcpy(&(header.size), current, sizeof(FrameSize)); + current += sizeof(FrameSize); header.fileOffset = current - start; if (end - current < header.size) { current = end; @@ -117,6 +117,7 @@ FileClip::FileClip(const QString& fileName) : _file(fileName) { qWarning() << "Header missing frame type map, invalid file"; return; } + qDebug() << translationMap; // Update the loaded headers with the frame data _frameHeaders.reserve(parsedFrameHeaders.size()); @@ -132,16 +133,21 @@ FileClip::FileClip(const QString& fileName) : _file(fileName) { // FIXME move to frame? bool writeFrame(QIODevice& output, const Frame& frame) { + if (frame.type == Frame::TYPE_INVALID) { + qWarning() << "Attempting to write invalid frame"; + return true; + } + auto written = output.write((char*)&(frame.type), sizeof(FrameType)); if (written != sizeof(FrameType)) { return false; } - written = output.write((char*)&(frame.timeOffset), sizeof(float)); - if (written != sizeof(float)) { + written = output.write((char*)&(frame.timeOffset), sizeof(Time)); + if (written != sizeof(Time)) { return false; } uint16_t dataSize = frame.data.size(); - written = output.write((char*)&dataSize, sizeof(uint16_t)); + written = output.write((char*)&dataSize, sizeof(FrameSize)); if (written != sizeof(uint16_t)) { return false; } @@ -201,19 +207,19 @@ FileClip::~FileClip() { } } -void FileClip::seek(float offset) { +void FileClip::seek(Time offset) { Locker lock(_mutex); auto itr = std::lower_bound(_frameHeaders.begin(), _frameHeaders.end(), offset, - [](const FrameHeader& a, float b)->bool { + [](const FrameHeader& a, Time b)->bool { return a.timeOffset < b; } ); _frameIndex = itr - _frameHeaders.begin(); } -float FileClip::position() const { +Time FileClip::position() const { Locker lock(_mutex); - float result = std::numeric_limits::max(); + Time result = INVALID_TIME; if (_frameIndex < _frameHeaders.size()) { result = _frameHeaders[_frameIndex].timeOffset; } @@ -260,7 +266,7 @@ void FileClip::addFrame(FramePointer) { throw std::runtime_error("File clips are read only"); } -float FileClip::duration() const { +Time FileClip::duration() const { if (_frameHeaders.empty()) { return 0; } diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h index 08eacd8337..78256dcf23 100644 --- a/libraries/recording/src/recording/impl/FileClip.h +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -26,11 +26,11 @@ public: FileClip(const QString& file); virtual ~FileClip(); - virtual float duration() const override; + virtual Time duration() const override; virtual size_t frameCount() const override; - virtual void seek(float offset) override; - virtual float position() const override; + virtual void seek(Time offset) override; + virtual Time position() const override; virtual FramePointer peekFrame() const override; virtual FramePointer nextFrame() override; @@ -45,7 +45,7 @@ public: struct FrameHeader { FrameType type; - float timeOffset; + Time timeOffset; uint16_t size; quint64 fileOffset; }; diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 5e3d135034..3796abd92a 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 controllers animation audio physics) +link_hifi_libraries(shared networking octree gpu procedural model model-networking recording avatars fbx entities controllers animation audio physics) diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp new file mode 100644 index 0000000000..dc872574fe --- /dev/null +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -0,0 +1,57 @@ +// +// Created by Bradley Austin Davis on 2015/11/09 +// Copyright 2013-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 "JSONHelpers.h" + +#include +#include +#include + +template +QJsonValue glmToJson(const T& t) { + static const T DEFAULT_VALUE = T(); + if (t == DEFAULT_VALUE) { + return QJsonValue(); + } + QJsonArray result; + for (auto i = 0; i < t.length(); ++i) { + result.push_back(t[i]); + } + return result; +} + +template +T glmFromJson(const QJsonValue& json) { + static const T DEFAULT_VALUE = T(); + T result; + if (json.isArray()) { + QJsonArray array = json.toArray(); + size_t length = std::min(array.size(), result.length()); + for (size_t i = 0; i < length; ++i) { + result[i] = (float)array[i].toDouble(); + } + } + return result; +} + +QJsonValue toJsonValue(const quat& q) { + return glmToJson(q); +} + +QJsonValue toJsonValue(const vec3& v) { + return glmToJson(v); +} + +quat quatFromJsonValue(const QJsonValue& q) { + return glmFromJson(q); +} + +vec3 vec3FromJsonValue(const QJsonValue& v) { + return glmFromJson(v); +} + diff --git a/libraries/shared/src/shared/JSONHelpers.h b/libraries/shared/src/shared/JSONHelpers.h new file mode 100644 index 0000000000..0eda8ac94a --- /dev/null +++ b/libraries/shared/src/shared/JSONHelpers.h @@ -0,0 +1,23 @@ +// +// Created by Bradley Austin Davis on 2015/11/09 +// Copyright 2013-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_JSONHelpers_h +#define hifi_Shared_JSONHelpers_h + +#include "../GLMHelpers.h" + +QJsonValue toJsonValue(const quat& q); + +QJsonValue toJsonValue(const vec3& q); + +quat quatFromJsonValue(const QJsonValue& q); + +vec3 vec3FromJsonValue(const QJsonValue& q); + +#endif diff --git a/libraries/shared/src/shared/UniformTransform.cpp b/libraries/shared/src/shared/UniformTransform.cpp new file mode 100644 index 0000000000..fdcf489154 --- /dev/null +++ b/libraries/shared/src/shared/UniformTransform.cpp @@ -0,0 +1,84 @@ +// +// Created by Bradley Austin Davis on 2015/11/09 +// Copyright 2013-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 "UniformTransform.h" + +#include "JSONHelpers.h" + +#include +#include +#include + +#include + +const float UniformTransform::DEFAULT_SCALE = 1.0f; + +std::shared_ptr UniformTransform::parseJson(const QJsonValue& basis) { + std::shared_ptr result = std::make_shared(); + result->fromJson(basis); + return result; +} + +static const QString JSON_TRANSLATION = QStringLiteral("translation"); +static const QString JSON_ROTATION = QStringLiteral("rotation"); +static const QString JSON_SCALE = QStringLiteral("scale"); + +void UniformTransform::fromJson(const QJsonValue& basisValue) { + if (!basisValue.isObject()) { + return; + } + QJsonObject basis = basisValue.toObject(); + if (basis.contains(JSON_ROTATION)) { + rotation = quatFromJsonValue(basis[JSON_ROTATION]); + } + if (basis.contains(JSON_TRANSLATION)) { + translation = vec3FromJsonValue(basis[JSON_TRANSLATION]); + } + if (basis.contains(JSON_SCALE)) { + scale = (float)basis[JSON_SCALE].toDouble(); + } +} + +glm::mat4 toMat4(const UniformTransform& transform) { + return glm::translate(glm::mat4(), transform.translation) * glm::mat4_cast(transform.rotation); +} + +UniformTransform fromMat4(const glm::mat4& m) { + UniformTransform result; + result.translation = vec3(m[3]); + result.rotation = glm::quat_cast(m); + return result; +} + +UniformTransform UniformTransform::relativeTransform(const UniformTransform& worldTransform) const { + UniformTransform result = fromMat4(glm::inverse(toMat4(*this)) * toMat4(worldTransform)); + result.scale = scale / worldTransform.scale; + return result; +} + +UniformTransform UniformTransform::worldTransform(const UniformTransform& relativeTransform) const { + UniformTransform result = fromMat4(toMat4(*this) * toMat4(relativeTransform)); + result.scale = relativeTransform.scale * scale; + return result; +} + +QJsonObject UniformTransform::toJson() const { + QJsonObject result; + auto json = toJsonValue(translation); + if (!json.isNull()) { + result[JSON_TRANSLATION] = json; + } + json = toJsonValue(rotation); + if (!json.isNull()) { + result[JSON_ROTATION] = json; + } + if (scale != DEFAULT_SCALE) { + result[JSON_SCALE] = scale; + } + return result; +} diff --git a/libraries/shared/src/shared/UniformTransform.h b/libraries/shared/src/shared/UniformTransform.h new file mode 100644 index 0000000000..5b46de531e --- /dev/null +++ b/libraries/shared/src/shared/UniformTransform.h @@ -0,0 +1,40 @@ +// +// Created by Bradley Austin Davis on 2015/11/09 +// Copyright 2013-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_UniformTransform_h +#define hifi_Shared_UniformTransform_h + +#include "../GLMHelpers.h" + +class QJsonValue; + +struct UniformTransform { + static const float DEFAULT_SCALE; + glm::vec3 translation; + glm::quat rotation; + float scale { DEFAULT_SCALE }; + + UniformTransform() {} + + UniformTransform(const glm::vec3& translation, const glm::quat& rotation, const float& scale) + : translation(translation), rotation(rotation), scale(scale) {} + + UniformTransform relativeTransform(const UniformTransform& worldTransform) const; + glm::vec3 relativeVector(const UniformTransform& worldTransform) const; + + UniformTransform worldTransform(const UniformTransform& relativeTransform) const; + glm::vec3 worldVector(const UniformTransform& relativeTransform) const; + + QJsonObject toJson() const; + void fromJson(const QJsonValue& json); + + static std::shared_ptr parseJson(const QJsonValue& json); +}; + +#endif From eefe26d96ab6192fa68d1cd661207be0325692e9 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 09:24:49 -0800 Subject: [PATCH 0884/1003] fix crash in Model::deleteGeometry when _rig is not initialized --- libraries/render-utils/src/Model.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5b9bfdca3d..700b88e97b 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1099,10 +1099,12 @@ void Model::setGeometry(const QSharedPointer& newGeometry) { void Model::deleteGeometry() { _blendedVertexBuffers.clear(); - _rig->clearJointStates(); _meshStates.clear(); - _rig->deleteAnimations(); - _rig->destroyAnimGraph(); + if (_rig) { + _rig->clearJointStates(); + _rig->deleteAnimations(); + _rig->destroyAnimGraph(); + } _blendedBlendshapeCoefficients.clear(); } From 93da3ed5bdc282b719983286bbcb5e8921de38ae Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 11:03:34 -0800 Subject: [PATCH 0885/1003] if our simulation-bid priority matches the remote one, don't bid. --- libraries/physics/src/EntityMotionState.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 9be6dd38b1..21b7187063 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -116,8 +116,8 @@ bool EntityMotionState::handleEasyChanges(uint32_t flags, PhysicsEngine* engine) _outgoingPriority = NO_PRORITY; } else { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; - if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() > _outgoingPriority) { - // we own the simulation or our priority looses to remote + if (engine->getSessionID() == _entity->getSimulatorID() || _entity->getSimulationPriority() >= _outgoingPriority) { + // we own the simulation or our priority looses to (or ties with) remote _outgoingPriority = NO_PRORITY; } } @@ -379,7 +379,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s // we don't own the simulation, but maybe we should... if (_outgoingPriority != NO_PRORITY) { if (_outgoingPriority < _entity->getSimulationPriority()) { - // our priority looses to remote, so we don't bother to bid + // our priority loses to remote, so we don't bother to bid _outgoingPriority = NO_PRORITY; return false; } From a366633f843e45178ceeae1f3cf74bc3c4fae2a7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 11:03:44 -0800 Subject: [PATCH 0886/1003] cleanups --- libraries/render/src/render/DrawStatus.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index c7a54b18d0..5e8fd74e5f 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -87,20 +87,22 @@ const gpu::PipelinePointer DrawStatus::getDrawItemStatusPipeline() { } void DrawStatus::setStatusIconMap(const gpu::TexturePointer& map) { - _statusIconMap = map; + _statusIconMap = map; } const gpu::TexturePointer DrawStatus::getStatusIconMap() const { - return _statusIconMap; + return _statusIconMap; } -void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { +void DrawStatus::run(const SceneContextPointer& sceneContext, + const RenderContextPointer& renderContext, + const ItemIDsBounds& inItems) { assert(renderContext->args); assert(renderContext->args->_viewFrustum); RenderArgs* args = renderContext->args; auto& scene = sceneContext->_scene; - const int NUM_STATUS_VEC4_PER_ITEM = 2; - const int VEC4_LENGTH = 4; + const int NUM_STATUS_VEC4_PER_ITEM = 2; + const int VEC4_LENGTH = 4; // FIrst thing, we collect the bound and the status for all the items we want to render int nbItems = 0; @@ -112,8 +114,8 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex _itemStatus = std::make_shared();; } - _itemBounds->resize((inItems.size() * sizeof(AABox))); - _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); + _itemBounds->resize((inItems.size() * sizeof(AABox))); + _itemStatus->resize((inItems.size() * NUM_STATUS_VEC4_PER_ITEM * sizeof(glm::vec4))); AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); for (auto& item : inItems) { @@ -148,7 +150,7 @@ void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContex } nbItems++; - itemAABox++; + itemAABox++; } } } From 2e635bd93638413fe9d66d14d7bb0bd576f1a85b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 11:14:25 -0800 Subject: [PATCH 0887/1003] cleanups --- libraries/physics/src/EntityMotionState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 21b7187063..bd744c8be3 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -535,7 +535,7 @@ void EntityMotionState::clearIncomingDirtyFlags() { } } -// virtual +// virtual quint8 EntityMotionState::getSimulationPriority() const { if (_entity) { return _entity->getSimulationPriority(); From 288ee0e9e865a6eef1d3dcc4ac5133bdeadebaf4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 11:39:32 -0800 Subject: [PATCH 0888/1003] add a NodeDisconnect packet --- libraries/networking/src/udt/PacketHeaders.cpp | 1 + libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 24034ff9b3..669556c74a 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -93,6 +93,7 @@ QString nameForPacketType(PacketType packetType) { PACKET_TYPE_NAME_LOOKUP(PacketType::EntityAdd); PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEdit); PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerConnectionToken); + PACKET_TYPE_NAME_LOOKUP(PacketType::NodeDisconnect) default: return QString("Type: ") + QString::number((int)packetType); } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 82d905bf28..a77b2dab18 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -79,7 +79,8 @@ enum class PacketType : uint8_t { AssetUpload, AssetUploadReply, AssetGetInfo, - AssetGetInfoReply + AssetGetInfoReply, + NodeDisconnect }; const int NUM_BYTES_MD5_HASH = 16; From ee1545f649a254dba61ac3e2a1c7156365a45b25 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 12 Nov 2015 11:28:23 -0800 Subject: [PATCH 0889/1003] Cleaning up clip and transform --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 74 +++++++++------- libraries/avatars/src/AvatarData.h | 11 +-- libraries/recording/src/recording/Clip.cpp | 2 +- libraries/recording/src/recording/Clip.h | 7 +- libraries/recording/src/recording/Deck.h | 4 - libraries/recording/src/recording/Forward.h | 2 + libraries/recording/src/recording/Frame.cpp | 2 +- libraries/recording/src/recording/Frame.h | 5 +- .../src/recording/impl/BufferClip.cpp | 12 +-- .../recording/src/recording/impl/BufferClip.h | 8 +- .../recording/src/recording/impl/FileClip.cpp | 6 +- .../recording/src/recording/impl/FileClip.h | 6 +- .../src/recording/impl/OffsetClip.cpp | 51 +++++++++++ .../recording/src/recording/impl/OffsetClip.h | 37 ++++++++ .../src/recording/impl/WrapperClip.cpp | 60 +++++++++++++ .../src/recording/impl/WrapperClip.h | 48 +++++++++++ libraries/shared/src/NumericalConstants.h | 3 + libraries/shared/src/Transform.cpp | 73 ++++++++++++++++ libraries/shared/src/Transform.h | 29 +++++++ libraries/shared/src/shared/JSONHelpers.cpp | 3 + .../shared/src/shared/UniformTransform.cpp | 84 ------------------- .../shared/src/shared/UniformTransform.h | 40 --------- 23 files changed, 381 insertions(+), 188 deletions(-) create mode 100644 libraries/recording/src/recording/impl/OffsetClip.cpp create mode 100644 libraries/recording/src/recording/impl/OffsetClip.h create mode 100644 libraries/recording/src/recording/impl/WrapperClip.cpp create mode 100644 libraries/recording/src/recording/impl/WrapperClip.h delete mode 100644 libraries/shared/src/shared/UniformTransform.cpp delete mode 100644 libraries/shared/src/shared/UniformTransform.h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 852e1d1389..5e14c66ff1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -126,7 +126,7 @@ MyAvatar::MyAvatar(RigPointer rig) : }); // FIXME how to deal with driving multiple avatars locally? - Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::Pointer frame) { + Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::ConstPointer frame) { qDebug() << "Playback of avatar frame length: " << frame->data.size(); avatarStateFromFrame(frame->data, this); }); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c4e356d402..adf471c50e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -9,6 +9,9 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + +#include "AvatarData.h" + #include #include #include @@ -22,6 +25,7 @@ #include #include +#include #include #include #include @@ -29,12 +33,10 @@ #include #include #include -#include #include #include #include "AvatarLogging.h" -#include "AvatarData.h" quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND; @@ -908,7 +910,7 @@ void AvatarData::play() { } } -std::shared_ptr AvatarData::getRecordingBasis() const { +std::shared_ptr AvatarData::getRecordingBasis() const { return _recordingBasis; } @@ -1506,12 +1508,12 @@ void registerAvatarTypes(QScriptEngine* engine) { new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); } -void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { +void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { if (!recordingBasis) { - recordingBasis = std::make_shared(); - recordingBasis->rotation = getOrientation(); - recordingBasis->translation = getPosition(); - recordingBasis->scale = getTargetScale(); + recordingBasis = std::make_shared(); + recordingBasis->setRotation(getOrientation()); + recordingBasis->setTranslation(getPosition()); + recordingBasis->setScale(getTargetScale()); } _recordingBasis = recordingBasis; } @@ -1520,6 +1522,14 @@ void AvatarData::clearRecordingBasis() { _recordingBasis.reset(); } +Transform AvatarData::getTransform() const { + Transform result; + result.setRotation(getOrientation()); + result.setTranslation(getPosition()); + result.setScale(getTargetScale()); + return result; +} + static const QString JSON_AVATAR_BASIS = QStringLiteral("basisTransform"); static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform"); static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations"); @@ -1557,15 +1567,14 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) { auto recordingBasis = _avatar->getRecordingBasis(); if (recordingBasis) { - // FIXME if the resulting relative basis is identity, we shouldn't record anything - // Record the transformation basis - root[JSON_AVATAR_BASIS] = recordingBasis->toJson(); + // Find the relative transform + auto relativeTransform = recordingBasis->relativeTransform(_avatar->getTransform()); - // Record the relative transform - auto relativeTransform = recordingBasis->relativeTransform( - UniformTransform(_avatar->getPosition(), _avatar->getOrientation(), _avatar->getTargetScale())); - - root[JSON_AVATAR_RELATIVE] = relativeTransform.toJson(); + // if the resulting relative basis is identity, we shouldn't record anything + if (!relativeTransform.isIdentity()) { + root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); + root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); + } } QJsonArray jointRotations; @@ -1617,22 +1626,25 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { } } - // During playback you can either have the recording basis set to the avatar current state - // meaning that all playback is relative to this avatars starting position, or - // the basis can be loaded from the recording, meaning the playback is relative to the - // original avatar location - // The first is more useful for playing back recordings on your own avatar, while - // the latter is more useful for playing back other avatars within your scene. - auto currentBasis = _avatar->getRecordingBasis(); - if (!currentBasis) { - currentBasis = UniformTransform::parseJson(root[JSON_AVATAR_BASIS]); - } + if (root.contains(JSON_AVATAR_RELATIVE)) { + // During playback you can either have the recording basis set to the avatar current state + // meaning that all playback is relative to this avatars starting position, or + // the basis can be loaded from the recording, meaning the playback is relative to the + // original avatar location + // The first is more useful for playing back recordings on your own avatar, while + // the latter is more useful for playing back other avatars within your scene. - auto relativeTransform = UniformTransform::parseJson(root[JSON_AVATAR_RELATIVE]); - auto worldTransform = currentBasis->worldTransform(*relativeTransform); - _avatar->setPosition(worldTransform.translation); - _avatar->setOrientation(worldTransform.rotation); - _avatar->setTargetScale(worldTransform.scale); + auto currentBasis = _avatar->getRecordingBasis(); + if (!currentBasis) { + currentBasis = std::make_shared(Transform::fromJson(root[JSON_AVATAR_BASIS])); + } + + auto relativeTransform = Transform::fromJson(root[JSON_AVATAR_RELATIVE]); + auto worldTransform = currentBasis->worldTransform(relativeTransform); + _avatar->setPosition(worldTransform.getTranslation()); + _avatar->setOrientation(worldTransform.getRotation()); + _avatar->setTargetScale(worldTransform.getScale().x); + } #if 0 if (root.contains(JSON_AVATAR_ATTACHEMENTS)) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index ebb6e1a78f..7716a16a1c 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -134,7 +134,8 @@ class QDataStream; class AttachmentData; class JointData; -struct UniformTransform; +class Transform; +using TransformPointer = std::shared_ptr; class AvatarData : public QObject { Q_OBJECT @@ -333,10 +334,10 @@ public: bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; } + Transform getTransform() const; void clearRecordingBasis(); - std::shared_ptr getRecordingBasis() const; - void setRecordingBasis(std::shared_ptr recordingBasis = std::shared_ptr()); - + TransformPointer getRecordingBasis() const; + void setRecordingBasis(TransformPointer recordingBasis = TransformPointer()); public slots: void sendAvatarDataPacket(); @@ -437,7 +438,7 @@ protected: // During recording, this holds the starting position, orientation & scale of the recorded avatar // During playback, it holds the - std::shared_ptr _recordingBasis; + TransformPointer _recordingBasis; private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index ba13b359f0..28e4211fe3 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -38,7 +38,7 @@ Clip::Pointer Clip::duplicate() { Time currentPosition = position(); seek(0); - Frame::Pointer frame = nextFrame(); + auto frame = nextFrame(); while (frame) { result->addFrame(frame); frame = nextFrame(); diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index 70fc4b3f7f..a00ab72c98 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -34,16 +34,17 @@ public: virtual void seek(Time offset) = 0; virtual Time position() const = 0; - virtual FramePointer peekFrame() const = 0; - virtual FramePointer nextFrame() = 0; + virtual FrameConstPointer peekFrame() const = 0; + virtual FrameConstPointer nextFrame() = 0; virtual void skipFrame() = 0; - virtual void addFrame(FramePointer) = 0; + virtual void addFrame(FrameConstPointer) = 0; static Pointer fromFile(const QString& filePath); static void toFile(const QString& filePath, Pointer clip); static Pointer newClip(); protected: + friend class WrapperClip; using Mutex = std::recursive_mutex; using Locker = std::unique_lock; diff --git a/libraries/recording/src/recording/Deck.h b/libraries/recording/src/recording/Deck.h index a3b4405210..7086e9759d 100644 --- a/libraries/recording/src/recording/Deck.h +++ b/libraries/recording/src/recording/Deck.h @@ -46,9 +46,6 @@ public: Time position() const; void seek(Time position); - void setPlaybackSpeed(float factor) { _playbackSpeed = factor; } - float getPlaybackSpeed() { return _playbackSpeed; } - signals: void playbackStateChanged(); @@ -62,7 +59,6 @@ private: Clips _clips; quint64 _startEpoch { 0 }; Time _position { 0 }; - float _playbackSpeed { 1.0f }; bool _pause { true }; bool _loop { false }; Time _length { 0 }; diff --git a/libraries/recording/src/recording/Forward.h b/libraries/recording/src/recording/Forward.h index 31aa40521c..4ba54e23a3 100644 --- a/libraries/recording/src/recording/Forward.h +++ b/libraries/recording/src/recording/Forward.h @@ -28,6 +28,8 @@ struct Frame; using FramePointer = std::shared_ptr; +using FrameConstPointer = std::shared_ptr; + // A recording of some set of state from the application, usually avatar // data + audio for a single person class Clip; diff --git a/libraries/recording/src/recording/Frame.cpp b/libraries/recording/src/recording/Frame.cpp index ad2a583424..5b0116519f 100644 --- a/libraries/recording/src/recording/Frame.cpp +++ b/libraries/recording/src/recording/Frame.cpp @@ -104,7 +104,7 @@ Frame::Handler Frame::registerFrameHandler(FrameType type, Handler handler) { return result; } -void Frame::handleFrame(const Frame::Pointer& frame) { +void Frame::handleFrame(const Frame::ConstPointer& frame) { Handler handler; { Locker lock(mutex); diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index 563b042656..85f5246a4e 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -21,7 +21,8 @@ namespace recording { struct Frame { public: using Pointer = std::shared_ptr; - using Handler = std::function; + using ConstPointer = std::shared_ptr; + using Handler = std::function; static const FrameType TYPE_INVALID = 0xFFFF; static const FrameType TYPE_HEADER = 0x0; @@ -37,7 +38,7 @@ public: static QMap getFrameTypes(); static QMap getFrameTypeNames(); static Handler registerFrameHandler(FrameType type, Handler handler); - static void handleFrame(const Pointer& frame); + static void handleFrame(const ConstPointer& frame); }; } diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index 5dc75bbce2..87bbfbfef7 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -18,7 +18,7 @@ using namespace recording; void BufferClip::seek(Time offset) { Locker lock(_mutex); auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, - [](Frame::Pointer a, Time b)->bool { + [](Frame::ConstPointer a, Time b)->bool { return a->timeOffset < b; } ); @@ -34,18 +34,18 @@ Time BufferClip::position() const { return result; } -FramePointer BufferClip::peekFrame() const { +FrameConstPointer BufferClip::peekFrame() const { Locker lock(_mutex); - FramePointer result; + FrameConstPointer result; if (_frameIndex < _frames.size()) { result = _frames[_frameIndex]; } return result; } -FramePointer BufferClip::nextFrame() { +FrameConstPointer BufferClip::nextFrame() { Locker lock(_mutex); - FramePointer result; + FrameConstPointer result; if (_frameIndex < _frames.size()) { result = _frames[_frameIndex]; ++_frameIndex; @@ -53,7 +53,7 @@ FramePointer BufferClip::nextFrame() { return result; } -void BufferClip::addFrame(FramePointer newFrame) { +void BufferClip::addFrame(FrameConstPointer newFrame) { if (newFrame->timeOffset < 0.0f) { throw std::runtime_error("Frames may not have negative time offsets"); } diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index bfb0234600..ce81dac730 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -28,15 +28,15 @@ public: virtual void seek(Time offset) override; virtual Time position() const override; - virtual FramePointer peekFrame() const override; - virtual FramePointer nextFrame() override; + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; virtual void skipFrame() override; - virtual void addFrame(FramePointer) override; + virtual void addFrame(FrameConstPointer) override; private: virtual void reset() override; - std::vector _frames; + std::vector _frames; mutable size_t _frameIndex { 0 }; }; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index e64085517a..b8e1eb26fa 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -240,12 +240,12 @@ FramePointer FileClip::readFrame(uint32_t frameIndex) const { return result; } -FramePointer FileClip::peekFrame() const { +FrameConstPointer FileClip::peekFrame() const { Locker lock(_mutex); return readFrame(_frameIndex); } -FramePointer FileClip::nextFrame() { +FrameConstPointer FileClip::nextFrame() { Locker lock(_mutex); auto result = readFrame(_frameIndex); if (_frameIndex < _frameHeaders.size()) { @@ -262,7 +262,7 @@ void FileClip::reset() { _frameIndex = 0; } -void FileClip::addFrame(FramePointer) { +void FileClip::addFrame(FrameConstPointer) { throw std::runtime_error("File clips are read only"); } diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h index 78256dcf23..18c62936c1 100644 --- a/libraries/recording/src/recording/impl/FileClip.h +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -32,10 +32,10 @@ public: virtual void seek(Time offset) override; virtual Time position() const override; - virtual FramePointer peekFrame() const override; - virtual FramePointer nextFrame() override; + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; virtual void skipFrame() override; - virtual void addFrame(FramePointer) override; + virtual void addFrame(FrameConstPointer) override; const QJsonDocument& getHeader() { return _fileHeader; diff --git a/libraries/recording/src/recording/impl/OffsetClip.cpp b/libraries/recording/src/recording/impl/OffsetClip.cpp new file mode 100644 index 0000000000..bccd48d6c8 --- /dev/null +++ b/libraries/recording/src/recording/impl/OffsetClip.cpp @@ -0,0 +1,51 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "OffsetClip.h" + +#include + +#include +#include +#include + +#include + +#include "../Frame.h" +#include "../Logging.h" + + +using namespace recording; + +OffsetClip::OffsetClip(const Clip::Pointer& wrappedClip, Time offset) + : WrapperClip(wrappedClip), _offset(offset) { } + +void OffsetClip::seek(Time offset) { + _wrappedClip->seek(offset - _offset); +} + +Time OffsetClip::position() const { + return _wrappedClip->position() + _offset; +} + +FrameConstPointer OffsetClip::peekFrame() const { + auto result = std::make_shared(*_wrappedClip->peekFrame()); + result->timeOffset += _offset; + return result; +} + +FrameConstPointer OffsetClip::nextFrame() { + auto result = std::make_shared(*_wrappedClip->nextFrame()); + result->timeOffset += _offset; + return result; +} + +Time OffsetClip::duration() const { + return _wrappedClip->duration() + _offset; +} + diff --git a/libraries/recording/src/recording/impl/OffsetClip.h b/libraries/recording/src/recording/impl/OffsetClip.h new file mode 100644 index 0000000000..1c6b005b65 --- /dev/null +++ b/libraries/recording/src/recording/impl/OffsetClip.h @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Recording_Impl_OffsetClip_h +#define hifi_Recording_Impl_OffsetClip_h + +#include "WrapperClip.h" + +namespace recording { + +class OffsetClip : public WrapperClip { +public: + using Pointer = std::shared_ptr; + + OffsetClip(const Clip::Pointer& wrappedClip, Time offset); + virtual ~OffsetClip(); + + virtual Time duration() const override; + virtual void seek(Time offset) override; + virtual Time position() const override; + + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; + +protected: + const Time _offset; +}; + +} + +#endif diff --git a/libraries/recording/src/recording/impl/WrapperClip.cpp b/libraries/recording/src/recording/impl/WrapperClip.cpp new file mode 100644 index 0000000000..f2bbacabf1 --- /dev/null +++ b/libraries/recording/src/recording/impl/WrapperClip.cpp @@ -0,0 +1,60 @@ +// +// Created by Bradley Austin Davis 2015/11/04 +// 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 "WrapperClip.h" + + +#include + +#include + +#include "../Frame.h" +#include "../Logging.h" + + +using namespace recording; + +WrapperClip::WrapperClip(const Clip::Pointer& wrappedClip) + : _wrappedClip(wrappedClip) { } + +void WrapperClip::seek(Time offset) { + _wrappedClip->seek(offset); +} + +Time WrapperClip::position() const { + return _wrappedClip->position(); +} + +FrameConstPointer WrapperClip::peekFrame() const { + return _wrappedClip->peekFrame(); +} + +FrameConstPointer WrapperClip::nextFrame() { + return _wrappedClip->nextFrame(); +} + +void WrapperClip::skipFrame() { + _wrappedClip->skipFrame(); +} + +void WrapperClip::reset() { + _wrappedClip->reset(); +} + +void WrapperClip::addFrame(FrameConstPointer) { + throw std::runtime_error("Wrapper clips are read only"); +} + +Time WrapperClip::duration() const { + return _wrappedClip->duration(); +} + +size_t WrapperClip::frameCount() const { + return _wrappedClip->frameCount(); +} + diff --git a/libraries/recording/src/recording/impl/WrapperClip.h b/libraries/recording/src/recording/impl/WrapperClip.h new file mode 100644 index 0000000000..3fe013e0ed --- /dev/null +++ b/libraries/recording/src/recording/impl/WrapperClip.h @@ -0,0 +1,48 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Recording_Impl_WrapperClip_h +#define hifi_Recording_Impl_WrapperClip_h + +#include "../Clip.h" + +#include +#include + +#include + +namespace recording { + +class WrapperClip : public Clip { +public: + using Pointer = std::shared_ptr; + + WrapperClip(const Clip::Pointer& wrappedClip); + virtual ~WrapperClip(); + + virtual Time duration() const override; + virtual size_t frameCount() const override; + + virtual void seek(Time offset) override; + virtual Time position() const override; + + virtual FrameConstPointer peekFrame() const override; + virtual FrameConstPointer nextFrame() override; + virtual void skipFrame() override; + virtual void addFrame(FrameConstPointer) override; + +protected: + virtual void reset() override; + + const Clip::Pointer _wrappedClip; +}; + +} + +#endif diff --git a/libraries/shared/src/NumericalConstants.h b/libraries/shared/src/NumericalConstants.h index 9a946e35f7..6b37031d23 100644 --- a/libraries/shared/src/NumericalConstants.h +++ b/libraries/shared/src/NumericalConstants.h @@ -23,6 +23,9 @@ const float TWO_PI = 2.0f * PI; const float PI_OVER_TWO = 0.5f * PI; const float RADIANS_PER_DEGREE = PI / 180.0f; const float DEGREES_PER_RADIAN = 180.0f / PI; +const float ARCMINUTES_PER_DEGREE = 60.0f; +const float ARCSECONDS_PER_ARCMINUTE = 60.0f; +const float ARCSECONDS_PER_DEGREE = ARCMINUTES_PER_DEGREE * ARCSECONDS_PER_ARCMINUTE; const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations const float SQUARE_ROOT_OF_2 = (float)sqrt(2.0f); diff --git a/libraries/shared/src/Transform.cpp b/libraries/shared/src/Transform.cpp index 4b2a481f41..1358f396d9 100644 --- a/libraries/shared/src/Transform.cpp +++ b/libraries/shared/src/Transform.cpp @@ -11,6 +11,12 @@ #include "Transform.h" +#include +#include +#include + +#include "NumericalConstants.h" +#include "shared/JSONHelpers.h" void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix) { const float ACCURACY_THREASHOLD = 0.00001f; @@ -65,7 +71,74 @@ void Transform::evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotat rotation = (glm::quat_cast(matRot)); } +Transform Transform::relativeTransform(const Transform& worldTransform) const { + if (isIdentity()) { + return worldTransform; + } + if (*this == worldTransform) { + return Transform(); + } + Transform result; + inverseMult(result, *this, worldTransform); + return result; +} + +Transform Transform::worldTransform(const Transform& relativeTransform) const { + if (relativeTransform.isIdentity()) { + return *this; + } + + if (isIdentity()) { + return relativeTransform; + } + + Transform result; + mult(result, *this, relativeTransform); + return result; +} +static const QString JSON_TRANSLATION = QStringLiteral("translation"); +static const QString JSON_ROTATION = QStringLiteral("rotation"); +static const QString JSON_SCALE = QStringLiteral("scale"); + +Transform Transform::fromJson(const QJsonValue& json) { + if (!json.isObject()) { + return Transform(); + } + QJsonObject obj = json.toObject(); + Transform result; + if (obj.contains(JSON_ROTATION)) { + result.setRotation(quatFromJsonValue(obj[JSON_ROTATION])); + } + if (obj.contains(JSON_TRANSLATION)) { + result.setTranslation(vec3FromJsonValue(obj[JSON_TRANSLATION])); + } + if (obj.contains(JSON_SCALE)) { + result.setScale(vec3FromJsonValue(obj[JSON_TRANSLATION])); + } + return result; +} + +QJsonObject Transform::toJson(const Transform& transform) { + if (transform.isIdentity()) { + return QJsonObject(); + } + + QJsonObject result; + auto json = toJsonValue(transform.getTranslation()); + if (!json.isNull()) { + result[JSON_TRANSLATION] = json; + } + json = toJsonValue(transform.getRotation()); + if (!json.isNull()) { + result[JSON_ROTATION] = json; + } + json = toJsonValue(transform.getScale()); + if (!json.isNull()) { + result[JSON_SCALE] = json; + } + return result; +} diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index bf3b336e90..ec81513f3e 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -21,6 +21,9 @@ #include +class QJsonObject; +class QJsonValue; + inline bool isValidScale(glm::vec3 scale) { bool result = scale.x != 0.0f && scale.y != 0.0f && scale.z != 0.0f; assert(result); @@ -35,6 +38,7 @@ inline bool isValidScale(float scale) { class Transform { public: + using Pointer = std::shared_ptr; typedef glm::mat4 Mat4; typedef glm::mat3 Mat3; typedef glm::vec4 Vec4; @@ -81,6 +85,10 @@ public: return (*this); } + bool operator==(const Transform& other) const { + return _rotation == other._rotation && _scale == other._scale && _translation == other._translation; + } + Transform& setIdentity(); const Vec3& getTranslation() const; @@ -111,6 +119,8 @@ public: Transform& evalFromRawMatrix(const Mat4& matrix); Transform& evalFromRawMatrix(const Mat3& rotationScalematrix); + Mat4 getMatrix() const; + Mat4 getInverseMatrix() const; Mat4& getMatrix(Mat4& result) const; Mat4& getInverseMatrix(Mat4& result) const; Mat4& getInverseTransposeMatrix(Mat4& result) const; @@ -120,6 +130,9 @@ public: Transform& evalInverse(Transform& result) const; + Transform relativeTransform(const Transform& world) const; + Transform worldTransform(const Transform& relative) const; + static void evalRotationScale(Quat& rotation, Vec3& scale, const Mat3& rotationScaleMatrix); static Transform& mult(Transform& result, const Transform& left, const Transform& right); @@ -127,6 +140,10 @@ public: // Left will be inversed before the multiplication static Transform& inverseMult(Transform& result, const Transform& left, const Transform& right); + + static Transform fromJson(const QJsonValue& json); + static QJsonObject toJson(const Transform& transform); + Vec4 transform(const Vec4& pos) const; Vec3 transform(const Vec3& pos) const; @@ -368,6 +385,18 @@ inline Transform& Transform::postScale(const Vec3& scale) { return *this; } +inline Transform::Mat4 Transform::getMatrix() const { + Transform::Mat4 result; + getMatrix(result); + return result; +} + +inline Transform::Mat4 Transform::getInverseMatrix() const { + Transform::Mat4 result; + getInverseMatrix(result); + return result; +} + inline Transform::Mat4& Transform::getMatrix(Transform::Mat4& result) const { if (isRotating()) { Mat3 rot = glm::mat3_cast(_rotation); diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index dc872574fe..52ece73490 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -52,6 +52,9 @@ quat quatFromJsonValue(const QJsonValue& q) { } vec3 vec3FromJsonValue(const QJsonValue& v) { + if (v.isDouble()) { + return vec3((float)v.toDouble()); + } return glmFromJson(v); } diff --git a/libraries/shared/src/shared/UniformTransform.cpp b/libraries/shared/src/shared/UniformTransform.cpp deleted file mode 100644 index fdcf489154..0000000000 --- a/libraries/shared/src/shared/UniformTransform.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/11/09 -// Copyright 2013-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 "UniformTransform.h" - -#include "JSONHelpers.h" - -#include -#include -#include - -#include - -const float UniformTransform::DEFAULT_SCALE = 1.0f; - -std::shared_ptr UniformTransform::parseJson(const QJsonValue& basis) { - std::shared_ptr result = std::make_shared(); - result->fromJson(basis); - return result; -} - -static const QString JSON_TRANSLATION = QStringLiteral("translation"); -static const QString JSON_ROTATION = QStringLiteral("rotation"); -static const QString JSON_SCALE = QStringLiteral("scale"); - -void UniformTransform::fromJson(const QJsonValue& basisValue) { - if (!basisValue.isObject()) { - return; - } - QJsonObject basis = basisValue.toObject(); - if (basis.contains(JSON_ROTATION)) { - rotation = quatFromJsonValue(basis[JSON_ROTATION]); - } - if (basis.contains(JSON_TRANSLATION)) { - translation = vec3FromJsonValue(basis[JSON_TRANSLATION]); - } - if (basis.contains(JSON_SCALE)) { - scale = (float)basis[JSON_SCALE].toDouble(); - } -} - -glm::mat4 toMat4(const UniformTransform& transform) { - return glm::translate(glm::mat4(), transform.translation) * glm::mat4_cast(transform.rotation); -} - -UniformTransform fromMat4(const glm::mat4& m) { - UniformTransform result; - result.translation = vec3(m[3]); - result.rotation = glm::quat_cast(m); - return result; -} - -UniformTransform UniformTransform::relativeTransform(const UniformTransform& worldTransform) const { - UniformTransform result = fromMat4(glm::inverse(toMat4(*this)) * toMat4(worldTransform)); - result.scale = scale / worldTransform.scale; - return result; -} - -UniformTransform UniformTransform::worldTransform(const UniformTransform& relativeTransform) const { - UniformTransform result = fromMat4(toMat4(*this) * toMat4(relativeTransform)); - result.scale = relativeTransform.scale * scale; - return result; -} - -QJsonObject UniformTransform::toJson() const { - QJsonObject result; - auto json = toJsonValue(translation); - if (!json.isNull()) { - result[JSON_TRANSLATION] = json; - } - json = toJsonValue(rotation); - if (!json.isNull()) { - result[JSON_ROTATION] = json; - } - if (scale != DEFAULT_SCALE) { - result[JSON_SCALE] = scale; - } - return result; -} diff --git a/libraries/shared/src/shared/UniformTransform.h b/libraries/shared/src/shared/UniformTransform.h deleted file mode 100644 index 5b46de531e..0000000000 --- a/libraries/shared/src/shared/UniformTransform.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Created by Bradley Austin Davis on 2015/11/09 -// Copyright 2013-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_UniformTransform_h -#define hifi_Shared_UniformTransform_h - -#include "../GLMHelpers.h" - -class QJsonValue; - -struct UniformTransform { - static const float DEFAULT_SCALE; - glm::vec3 translation; - glm::quat rotation; - float scale { DEFAULT_SCALE }; - - UniformTransform() {} - - UniformTransform(const glm::vec3& translation, const glm::quat& rotation, const float& scale) - : translation(translation), rotation(rotation), scale(scale) {} - - UniformTransform relativeTransform(const UniformTransform& worldTransform) const; - glm::vec3 relativeVector(const UniformTransform& worldTransform) const; - - UniformTransform worldTransform(const UniformTransform& relativeTransform) const; - glm::vec3 worldVector(const UniformTransform& relativeTransform) const; - - QJsonObject toJson() const; - void fromJson(const QJsonValue& json); - - static std::shared_ptr parseJson(const QJsonValue& json); -}; - -#endif From 66a2b38f50ae9362aa69919f7469473bf97e23ee Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 13:09:51 -0800 Subject: [PATCH 0890/1003] switch various status to use thread safe atomics --- .../src/octree/OctreeSendThread.cpp | 10 +++---- .../src/octree/OctreeSendThread.h | 14 ++++++---- libraries/octree/src/OctreeElement.cpp | 22 +++++++-------- libraries/octree/src/OctreeElement.h | 27 +++++++++++-------- libraries/octree/src/OctreePacketData.cpp | 18 ++++++------- libraries/octree/src/OctreePacketData.h | 20 ++++++++------ libraries/shared/src/PerfStat.h | 8 +++--- 7 files changed, 66 insertions(+), 53 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index b94317050c..c6f84b9a59 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -112,12 +112,12 @@ bool OctreeSendThread::process() { return isStillRunning(); // keep running till they terminate us } -quint64 OctreeSendThread::_usleepTime = 0; -quint64 OctreeSendThread::_usleepCalls = 0; +AtomicUIntStat OctreeSendThread::_usleepTime = 0; +AtomicUIntStat OctreeSendThread::_usleepCalls = 0; -quint64 OctreeSendThread::_totalBytes = 0; -quint64 OctreeSendThread::_totalWastedBytes = 0; -quint64 OctreeSendThread::_totalPackets = 0; +AtomicUIntStat OctreeSendThread::_totalBytes = 0; +AtomicUIntStat OctreeSendThread::_totalWastedBytes = 0; +AtomicUIntStat OctreeSendThread::_totalPackets = 0; int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { OctreeServer::didHandlePacketSend(this); diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 78f480b5eb..69ea0be567 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -14,6 +14,8 @@ #ifndef hifi_OctreeSendThread_h #define hifi_OctreeSendThread_h +#include + #include #include @@ -21,6 +23,8 @@ class OctreeServer; +using AtomicUIntStat = std::atomic; + /// Threaded processor for sending octree packets to a single client class OctreeSendThread : public GenericThread { Q_OBJECT @@ -30,12 +34,12 @@ public: void setIsShuttingDown(); - static quint64 _totalBytes; - static quint64 _totalWastedBytes; - static quint64 _totalPackets; + static AtomicUIntStat _totalBytes; + static AtomicUIntStat _totalWastedBytes; + static AtomicUIntStat _totalPackets; - static quint64 _usleepTime; - static quint64 _usleepCalls; + static AtomicUIntStat _usleepTime; + static AtomicUIntStat _usleepCalls; protected: /// Implements generic processing behavior for this thread. diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 17ebe492ce..6bb39871bb 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -28,11 +28,11 @@ #include "OctreeLogging.h" #include "SharedUtil.h" -quint64 OctreeElement::_octreeMemoryUsage = 0; -quint64 OctreeElement::_octcodeMemoryUsage = 0; -quint64 OctreeElement::_externalChildrenMemoryUsage = 0; -quint64 OctreeElement::_voxelNodeCount = 0; -quint64 OctreeElement::_voxelNodeLeafCount = 0; +AtomicUIntStat OctreeElement::_octreeMemoryUsage = 0; +AtomicUIntStat OctreeElement::_octcodeMemoryUsage = 0; +AtomicUIntStat OctreeElement::_externalChildrenMemoryUsage = 0; +AtomicUIntStat OctreeElement::_voxelNodeCount = 0; +AtomicUIntStat OctreeElement::_voxelNodeLeafCount = 0; void OctreeElement::resetPopulationStatistics() { _voxelNodeCount = 0; @@ -245,13 +245,13 @@ bool OctreeElement::isParentOf(OctreeElementPointer possibleChild) const { return false; } -quint64 OctreeElement::_getChildAtIndexTime = 0; -quint64 OctreeElement::_getChildAtIndexCalls = 0; -quint64 OctreeElement::_setChildAtIndexTime = 0; -quint64 OctreeElement::_setChildAtIndexCalls = 0; +AtomicUIntStat OctreeElement::_getChildAtIndexTime = 0; +AtomicUIntStat OctreeElement::_getChildAtIndexCalls = 0; +AtomicUIntStat OctreeElement::_setChildAtIndexTime = 0; +AtomicUIntStat OctreeElement::_setChildAtIndexCalls = 0; -quint64 OctreeElement::_externalChildrenCount = 0; -quint64 OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +AtomicUIntStat OctreeElement::_externalChildrenCount = 0; +AtomicUIntStat OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; OctreeElementPointer OctreeElement::getChildAtIndex(int childIndex) const { #ifdef SIMPLE_CHILD_ARRAY diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 6ccef31d4f..d705b64acd 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -15,6 +15,8 @@ //#define SIMPLE_CHILD_ARRAY #define SIMPLE_EXTERNAL_CHILDREN +#include + #include #include @@ -24,6 +26,9 @@ #include "ViewFrustum.h" #include "OctreeConstants.h" +using AtomicUIntStat = std::atomic; + + class EncodeBitstreamParams; class Octree; class OctreeElement; @@ -290,20 +295,20 @@ protected: //static QReadWriteLock _updateHooksLock; static std::vector _updateHooks; - static quint64 _voxelNodeCount; - static quint64 _voxelNodeLeafCount; + static AtomicUIntStat _voxelNodeCount; + static AtomicUIntStat _voxelNodeLeafCount; - static quint64 _octreeMemoryUsage; - static quint64 _octcodeMemoryUsage; - static quint64 _externalChildrenMemoryUsage; + static AtomicUIntStat _octreeMemoryUsage; + static AtomicUIntStat _octcodeMemoryUsage; + static AtomicUIntStat _externalChildrenMemoryUsage; - static quint64 _getChildAtIndexTime; - static quint64 _getChildAtIndexCalls; - static quint64 _setChildAtIndexTime; - static quint64 _setChildAtIndexCalls; + static AtomicUIntStat _getChildAtIndexTime; + static AtomicUIntStat _getChildAtIndexCalls; + static AtomicUIntStat _setChildAtIndexTime; + static AtomicUIntStat _setChildAtIndexCalls; - static quint64 _externalChildrenCount; - static quint64 _childrenCount[NUMBER_OF_CHILDREN + 1]; + static AtomicUIntStat _externalChildrenCount; + static AtomicUIntStat _childrenCount[NUMBER_OF_CHILDREN + 1]; }; #endif // hifi_OctreeElement_h diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 8430e21d79..5ed891fc7c 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -16,14 +16,12 @@ #include "OctreePacketData.h" bool OctreePacketData::_debug = false; -quint64 OctreePacketData::_totalBytesOfOctalCodes = 0; -quint64 OctreePacketData::_totalBytesOfBitMasks = 0; -quint64 OctreePacketData::_totalBytesOfColor = 0; -quint64 OctreePacketData::_totalBytesOfValues = 0; -quint64 OctreePacketData::_totalBytesOfPositions = 0; -quint64 OctreePacketData::_totalBytesOfRawData = 0; - - +AtomicUIntStat OctreePacketData::_totalBytesOfOctalCodes = 0; +AtomicUIntStat OctreePacketData::_totalBytesOfBitMasks = 0; +AtomicUIntStat OctreePacketData::_totalBytesOfColor = 0; +AtomicUIntStat OctreePacketData::_totalBytesOfValues = 0; +AtomicUIntStat OctreePacketData::_totalBytesOfPositions = 0; +AtomicUIntStat OctreePacketData::_totalBytesOfRawData = 0; OctreePacketData::OctreePacketData(bool enableCompression, int targetSize) { changeSettings(enableCompression, targetSize); // does reset... @@ -490,8 +488,8 @@ bool OctreePacketData::appendRawData(QByteArray data) { } -quint64 OctreePacketData::_compressContentTime = 0; -quint64 OctreePacketData::_compressContentCalls = 0; +AtomicUIntStat OctreePacketData::_compressContentTime = 0; +AtomicUIntStat OctreePacketData::_compressContentCalls = 0; bool OctreePacketData::compressContent() { PerformanceWarning warn(false, "OctreePacketData::compressContent()", false, &_compressContentTime, &_compressContentCalls); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 44737d37da..2c86d518ad 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -22,6 +22,8 @@ #ifndef hifi_OctreePacketData_h #define hifi_OctreePacketData_h +#include + #include #include #include @@ -35,6 +37,8 @@ #include "OctreeConstants.h" #include "OctreeElement.h" +using AtomicUIntStat = std::atomic; + typedef unsigned char OCTREE_PACKET_FLAGS; typedef uint16_t OCTREE_PACKET_SEQUENCE; const uint16_t MAX_OCTREE_PACKET_SEQUENCE = 65535; @@ -286,15 +290,15 @@ private: static bool _debug; - static quint64 _compressContentTime; - static quint64 _compressContentCalls; + static AtomicUIntStat _compressContentTime; + static AtomicUIntStat _compressContentCalls; - static quint64 _totalBytesOfOctalCodes; - static quint64 _totalBytesOfBitMasks; - static quint64 _totalBytesOfColor; - static quint64 _totalBytesOfValues; - static quint64 _totalBytesOfPositions; - static quint64 _totalBytesOfRawData; + static AtomicUIntStat _totalBytesOfOctalCodes; + static AtomicUIntStat _totalBytesOfBitMasks; + static AtomicUIntStat _totalBytesOfColor; + static AtomicUIntStat _totalBytesOfValues; + static AtomicUIntStat _totalBytesOfPositions; + static AtomicUIntStat _totalBytesOfRawData; }; #endif // hifi_OctreePacketData_h diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index 523bee0808..f8903a7ef3 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -24,19 +24,21 @@ #include #include +using AtomicUIntStat = std::atomic; + class PerformanceWarning { private: quint64 _start; const char* _message; bool _renderWarningsOn; bool _alwaysDisplay; - quint64* _runningTotal; - quint64* _totalCalls; + AtomicUIntStat* _runningTotal; + AtomicUIntStat* _totalCalls; static bool _suppressShortTimings; public: PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false, - quint64* runningTotal = NULL, quint64* totalCalls = NULL) : + AtomicUIntStat* runningTotal = NULL, AtomicUIntStat* totalCalls = NULL) : _start(usecTimestampNow()), _message(message), _renderWarningsOn(renderWarnings), From 14a6e1fb6b043d27a1f2e3ff39b76525bf2da671 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 13:24:25 -0800 Subject: [PATCH 0891/1003] fix mac compiler error --- assignment-client/src/octree/OctreeSendThread.cpp | 11 +++++------ libraries/octree/src/OctreeElement.cpp | 11 +++++------ libraries/octree/src/OctreePacketData.cpp | 12 ++++++------ 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index c6f84b9a59..1906cb7979 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -112,12 +112,11 @@ bool OctreeSendThread::process() { return isStillRunning(); // keep running till they terminate us } -AtomicUIntStat OctreeSendThread::_usleepTime = 0; -AtomicUIntStat OctreeSendThread::_usleepCalls = 0; - -AtomicUIntStat OctreeSendThread::_totalBytes = 0; -AtomicUIntStat OctreeSendThread::_totalWastedBytes = 0; -AtomicUIntStat OctreeSendThread::_totalPackets = 0; +AtomicUIntStat OctreeSendThread::_usleepTime { 0 }; +AtomicUIntStat OctreeSendThread::_usleepCalls { 0 }; +AtomicUIntStat OctreeSendThread::_totalBytes { 0 }; +AtomicUIntStat OctreeSendThread::_totalWastedBytes { 0 }; +AtomicUIntStat OctreeSendThread::_totalPackets { 0 }; int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { OctreeServer::didHandlePacketSend(this); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 6bb39871bb..48172bd9fe 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -245,12 +245,11 @@ bool OctreeElement::isParentOf(OctreeElementPointer possibleChild) const { return false; } -AtomicUIntStat OctreeElement::_getChildAtIndexTime = 0; -AtomicUIntStat OctreeElement::_getChildAtIndexCalls = 0; -AtomicUIntStat OctreeElement::_setChildAtIndexTime = 0; -AtomicUIntStat OctreeElement::_setChildAtIndexCalls = 0; - -AtomicUIntStat OctreeElement::_externalChildrenCount = 0; +AtomicUIntStat OctreeElement::_getChildAtIndexTime { 0 }; +AtomicUIntStat OctreeElement::_getChildAtIndexCalls { 0 }; +AtomicUIntStat OctreeElement::_setChildAtIndexTime { 0 }; +AtomicUIntStat OctreeElement::_setChildAtIndexCalls { 0 }; +AtomicUIntStat OctreeElement::_externalChildrenCount { 0 }; AtomicUIntStat OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; OctreeElementPointer OctreeElement::getChildAtIndex(int childIndex) const { diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 5ed891fc7c..7a255ad648 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -16,12 +16,12 @@ #include "OctreePacketData.h" bool OctreePacketData::_debug = false; -AtomicUIntStat OctreePacketData::_totalBytesOfOctalCodes = 0; -AtomicUIntStat OctreePacketData::_totalBytesOfBitMasks = 0; -AtomicUIntStat OctreePacketData::_totalBytesOfColor = 0; -AtomicUIntStat OctreePacketData::_totalBytesOfValues = 0; -AtomicUIntStat OctreePacketData::_totalBytesOfPositions = 0; -AtomicUIntStat OctreePacketData::_totalBytesOfRawData = 0; +AtomicUIntStat OctreePacketData::_totalBytesOfOctalCodes { 0 }; +AtomicUIntStat OctreePacketData::_totalBytesOfBitMasks { 0 }; +AtomicUIntStat OctreePacketData::_totalBytesOfColor { 0 }; +AtomicUIntStat OctreePacketData::_totalBytesOfValues { 0 }; +AtomicUIntStat OctreePacketData::_totalBytesOfPositions { 0 }; +AtomicUIntStat OctreePacketData::_totalBytesOfRawData { 0 }; OctreePacketData::OctreePacketData(bool enableCompression, int targetSize) { changeSettings(enableCompression, targetSize); // does reset... From 33110eda30e2e0eb91192b91a31f79e4dff1df98 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 13:25:50 -0800 Subject: [PATCH 0892/1003] fix mac compiler error --- libraries/octree/src/OctreeElement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 48172bd9fe..74d23f14cc 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -250,7 +250,7 @@ AtomicUIntStat OctreeElement::_getChildAtIndexCalls { 0 }; AtomicUIntStat OctreeElement::_setChildAtIndexTime { 0 }; AtomicUIntStat OctreeElement::_setChildAtIndexCalls { 0 }; AtomicUIntStat OctreeElement::_externalChildrenCount { 0 }; -AtomicUIntStat OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +AtomicUIntStat OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; OctreeElementPointer OctreeElement::getChildAtIndex(int childIndex) const { #ifdef SIMPLE_CHILD_ARRAY From 8bfb368a61021975ffd1d4fcfae06aa5049075a0 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 13:32:43 -0800 Subject: [PATCH 0893/1003] fix mac compiler error --- libraries/octree/src/OctreeElement.cpp | 10 +++++----- libraries/octree/src/OctreePacketData.cpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 74d23f14cc..2cff868035 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -28,11 +28,11 @@ #include "OctreeLogging.h" #include "SharedUtil.h" -AtomicUIntStat OctreeElement::_octreeMemoryUsage = 0; -AtomicUIntStat OctreeElement::_octcodeMemoryUsage = 0; -AtomicUIntStat OctreeElement::_externalChildrenMemoryUsage = 0; -AtomicUIntStat OctreeElement::_voxelNodeCount = 0; -AtomicUIntStat OctreeElement::_voxelNodeLeafCount = 0; +AtomicUIntStat OctreeElement::_octreeMemoryUsage { 0 }; +AtomicUIntStat OctreeElement::_octcodeMemoryUsage { 0 }; +AtomicUIntStat OctreeElement::_externalChildrenMemoryUsage { 0 }; +AtomicUIntStat OctreeElement::_voxelNodeCount { 0 }; +AtomicUIntStat OctreeElement::_voxelNodeLeafCount { 0 }; void OctreeElement::resetPopulationStatistics() { _voxelNodeCount = 0; diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 7a255ad648..811e96fcf4 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -488,8 +488,8 @@ bool OctreePacketData::appendRawData(QByteArray data) { } -AtomicUIntStat OctreePacketData::_compressContentTime = 0; -AtomicUIntStat OctreePacketData::_compressContentCalls = 0; +AtomicUIntStat OctreePacketData::_compressContentTime { 0 }; +AtomicUIntStat OctreePacketData::_compressContentCalls { 0 }; bool OctreePacketData::compressContent() { PerformanceWarning warn(false, "OctreePacketData::compressContent()", false, &_compressContentTime, &_compressContentCalls); From da651ac47555c0aa31c48e7406cedf45a61a54c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 13:47:27 -0800 Subject: [PATCH 0894/1003] fix mac compiler error --- libraries/octree/src/OctreeElement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 2cff868035..5f03627f1a 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -250,7 +250,7 @@ AtomicUIntStat OctreeElement::_getChildAtIndexCalls { 0 }; AtomicUIntStat OctreeElement::_setChildAtIndexTime { 0 }; AtomicUIntStat OctreeElement::_setChildAtIndexCalls { 0 }; AtomicUIntStat OctreeElement::_externalChildrenCount { 0 }; -AtomicUIntStat OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1] { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +AtomicUIntStat OctreeElement::_childrenCount[NUMBER_OF_CHILDREN + 1]; OctreeElementPointer OctreeElement::getChildAtIndex(int childIndex) const { #ifdef SIMPLE_CHILD_ARRAY From a8cd891e48f6b2a53aeeedfd6c9f34ee66a086d1 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 14:19:23 -0800 Subject: [PATCH 0895/1003] adjust how the decision to send action changes over the wire is made --- interface/src/avatar/AvatarActionHold.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 8 +++----- libraries/entities/src/EntityItem.h | 5 +++-- libraries/physics/src/EntityMotionState.cpp | 2 +- libraries/physics/src/ObjectActionOffset.cpp | 2 +- libraries/physics/src/ObjectActionSpring.cpp | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 866f444b32..0d86e79a2d 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -211,7 +211,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); + ownerEntity->setActionDataNeedsUpdate(true); } }); activateBody(); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 807186a304..ef39cf8063 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1740,8 +1740,6 @@ void EntityItem::deserializeActionsInternal() { } } - _actionDataDirty = true; - return; } @@ -1802,11 +1800,11 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { } const QByteArray EntityItem::getActionDataInternal() const { - if (_actionDataDirty) { + if (_actionDataNeedsUpdate) { bool success; serializeActions(success, _allActionsDataCache); if (success) { - _actionDataDirty = false; + _actionDataNeedsUpdate = false; } } return _allActionsDataCache; @@ -1816,7 +1814,7 @@ const QByteArray EntityItem::getActionData() const { QByteArray result; assertUnlocked(); - if (_actionDataDirty) { + if (_actionDataNeedsUpdate) { withWriteLock([&] { getActionDataInternal(); result = _allActionsDataCache; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index b3c976c1ca..6e4f3ac2ca 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -408,7 +408,8 @@ public: QList getActionIDs() { return _objectActions.keys(); } QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); - void setActionDataDirty(bool value) const { _actionDataDirty = value; } + void setActionDataNeedsUpdate(bool value) const { _actionDataNeedsUpdate = value; } + bool actionDataNeedsUpdate() const { return _actionDataNeedsUpdate; } bool shouldSuppressLocationEdits() const; void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; } @@ -511,7 +512,7 @@ protected: // are used to keep track of and work around this situation. void checkWaitingToRemove(EntitySimulation* simulation = nullptr); mutable QSet _actionsToRemove; - mutable bool _actionDataDirty = false; + mutable bool _actionDataNeedsUpdate = false; // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index bd744c8be3..841a41cb04 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -297,7 +297,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverPosition += dt * _serverVelocity; } - if (_serverActionData != _entity->getActionData()) { + if (_entity->actionDataNeedsUpdate()) { setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); return true; } diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index 2cfd98497b..be2f5b40a0 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -129,7 +129,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); + ownerEntity->setActionDataNeedsUpdate(true); } }); activateBody(); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index c1cd2db5ca..7289c88a8f 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -162,7 +162,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataDirty(true); + ownerEntity->setActionDataNeedsUpdate(true); } }); activateBody(); From 0a64242160e8d1b90c0573a4eed226951abaa423 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 14:43:25 -0800 Subject: [PATCH 0896/1003] cleanup string grabbing for PacketType enum --- ice-server/src/IceServer.cpp | 3 +- .../networking/src/udt/PacketHeaders.cpp | 61 +------- libraries/networking/src/udt/PacketHeaders.h | 130 ++++++++++-------- libraries/octree/src/Octree.cpp | 4 +- 4 files changed, 78 insertions(+), 120 deletions(-) diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index a6a28caa23..f65ff4a8cf 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -55,8 +55,7 @@ bool IceServer::packetVersionMatch(const udt::Packet& packet) { if (headerVersion == versionForPacketType(headerType)) { return true; } else { - qDebug() << "Packet version mismatch for packet" << headerType - << "(" << nameForPacketType(headerType) << ") from" << packet.getSenderSockAddr(); + qDebug() << "Packet version mismatch for packet" << headerType << " from" << packet.getSenderSockAddr(); return false; } diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 669556c74a..b389d4d3df 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -14,6 +14,7 @@ #include #include +#include const QSet NON_VERIFIED_PACKETS = QSet() << PacketType::NodeJsonStats << PacketType::EntityQuery @@ -46,60 +47,6 @@ PacketVersion versionForPacketType(PacketType packetType) { } } -#define PACKET_TYPE_NAME_LOOKUP(x) case x: return QString(#x); - -QString nameForPacketType(PacketType packetType) { - switch (packetType) { - PACKET_TYPE_NAME_LOOKUP(PacketType::Unknown); - PACKET_TYPE_NAME_LOOKUP(PacketType::StunResponse); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainList); - PACKET_TYPE_NAME_LOOKUP(PacketType::Ping); - PACKET_TYPE_NAME_LOOKUP(PacketType::PingReply); - PACKET_TYPE_NAME_LOOKUP(PacketType::KillAvatar); - PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarData); - PACKET_TYPE_NAME_LOOKUP(PacketType::InjectAudio); - PACKET_TYPE_NAME_LOOKUP(PacketType::MixedAudio); - PACKET_TYPE_NAME_LOOKUP(PacketType::MicrophoneAudioNoEcho); - PACKET_TYPE_NAME_LOOKUP(PacketType::MicrophoneAudioWithEcho); - PACKET_TYPE_NAME_LOOKUP(PacketType::BulkAvatarData); - PACKET_TYPE_NAME_LOOKUP(PacketType::SilentAudioFrame); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainListRequest); - PACKET_TYPE_NAME_LOOKUP(PacketType::RequestAssignment); - PACKET_TYPE_NAME_LOOKUP(PacketType::CreateAssignment); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainConnectionDenied); - PACKET_TYPE_NAME_LOOKUP(PacketType::MuteEnvironment); - PACKET_TYPE_NAME_LOOKUP(PacketType::AudioStreamStats); - PACKET_TYPE_NAME_LOOKUP(PacketType::OctreeStats); - PACKET_TYPE_NAME_LOOKUP(PacketType::Jurisdiction); - PACKET_TYPE_NAME_LOOKUP(PacketType::JurisdictionRequest); - PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarIdentity); - PACKET_TYPE_NAME_LOOKUP(PacketType::AvatarBillboard); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainConnectRequest); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerRequireDTLS); - PACKET_TYPE_NAME_LOOKUP(PacketType::NodeJsonStats); - PACKET_TYPE_NAME_LOOKUP(PacketType::EntityQuery); - PACKET_TYPE_NAME_LOOKUP(PacketType::EntityData); - PACKET_TYPE_NAME_LOOKUP(PacketType::EntityErase); - PACKET_TYPE_NAME_LOOKUP(PacketType::OctreeDataNack); - PACKET_TYPE_NAME_LOOKUP(PacketType::StopNode); - PACKET_TYPE_NAME_LOOKUP(PacketType::AudioEnvironment); - PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEditNack); - PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerHeartbeat); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerAddedNode); - PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerQuery); - PACKET_TYPE_NAME_LOOKUP(PacketType::ICEServerPeerInformation); - PACKET_TYPE_NAME_LOOKUP(PacketType::ICEPing); - PACKET_TYPE_NAME_LOOKUP(PacketType::ICEPingReply); - PACKET_TYPE_NAME_LOOKUP(PacketType::EntityAdd); - PACKET_TYPE_NAME_LOOKUP(PacketType::EntityEdit); - PACKET_TYPE_NAME_LOOKUP(PacketType::DomainServerConnectionToken); - PACKET_TYPE_NAME_LOOKUP(PacketType::NodeDisconnect) - default: - return QString("Type: ") + QString::number((int)packetType); - } - return QString("unexpected"); -} - uint qHash(const PacketType& key, uint seed) { // seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch to // strongly typed enum for PacketType @@ -107,6 +54,10 @@ uint qHash(const PacketType& key, uint seed) { } QDebug operator<<(QDebug debug, const PacketType& type) { - debug.nospace() << (uint8_t) type << " (" << qPrintable(nameForPacketType(type)) << ")"; + QMetaObject metaObject = PacketTypeEnum::staticMetaObject; + QMetaEnum metaEnum = metaObject.enumerator(metaObject.enumeratorOffset()); + QString typeName = metaEnum.valueToKey((int) type); + + debug.nospace().noquote() << (uint8_t) type << " (" << typeName << ")"; return debug.space(); } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index a77b2dab18..147c001ba9 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -18,71 +18,80 @@ #include #include +#include #include #include -// If adding a new packet packetType, you can replace one marked usable or add at the end. -// If you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well -// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t -enum class PacketType : uint8_t { - Unknown, - StunResponse, - DomainList, - Ping, - PingReply, - KillAvatar, - AvatarData, - InjectAudio, - MixedAudio, - MicrophoneAudioNoEcho, - MicrophoneAudioWithEcho, - BulkAvatarData, - SilentAudioFrame, - DomainListRequest, - RequestAssignment, - CreateAssignment, - DomainConnectionDenied, - MuteEnvironment, - AudioStreamStats, - DomainServerPathQuery, - DomainServerPathResponse, - DomainServerAddedNode, - ICEServerPeerInformation, - ICEServerQuery, - OctreeStats, - Jurisdiction, - JurisdictionRequest, - AssignmentClientStatus, - NoisyMute, - AvatarIdentity, - AvatarBillboard, - DomainConnectRequest, - DomainServerRequireDTLS, - NodeJsonStats, - OctreeDataNack, - StopNode, - AudioEnvironment, - EntityEditNack, - ICEServerHeartbeat, - ICEPing, - ICEPingReply, - EntityData, - EntityQuery, - EntityAdd, - EntityErase, - EntityEdit, - DomainServerConnectionToken, - DomainSettingsRequest, - DomainSettings, - AssetGet, - AssetGetReply, - AssetUpload, - AssetUploadReply, - AssetGetInfo, - AssetGetInfoReply, - NodeDisconnect +// The enums are inside this PacketTypeEnum for run-time conversion of enum value to string via +// Q_ENUMS, without requiring a macro that is called for each enum value. +class PacketTypeEnum { + Q_GADGET + Q_ENUMS(Value) +public: + // If adding a new packet packetType, you can replace one marked usable or add at the end. + // This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t + enum class Value : uint8_t { + Unknown, + StunResponse, + DomainList, + Ping, + PingReply, + KillAvatar, + AvatarData, + InjectAudio, + MixedAudio, + MicrophoneAudioNoEcho, + MicrophoneAudioWithEcho, + BulkAvatarData, + SilentAudioFrame, + DomainListRequest, + RequestAssignment, + CreateAssignment, + DomainConnectionDenied, + MuteEnvironment, + AudioStreamStats, + DomainServerPathQuery, + DomainServerPathResponse, + DomainServerAddedNode, + ICEServerPeerInformation, + ICEServerQuery, + OctreeStats, + Jurisdiction, + JurisdictionRequest, + AssignmentClientStatus, + NoisyMute, + AvatarIdentity, + AvatarBillboard, + DomainConnectRequest, + DomainServerRequireDTLS, + NodeJsonStats, + OctreeDataNack, + StopNode, + AudioEnvironment, + EntityEditNack, + ICEServerHeartbeat, + ICEPing, + ICEPingReply, + EntityData, + EntityQuery, + EntityAdd, + EntityErase, + EntityEdit, + DomainServerConnectionToken, + DomainSettingsRequest, + DomainSettings, + AssetGet, + AssetGetReply, + AssetUpload, + AssetUploadReply, + AssetGetInfo, + AssetGetInfoReply, + NodeDisconnect + }; }; +using PacketType = PacketTypeEnum::Value; + const int NUM_BYTES_MD5_HASH = 16; typedef char PacketVersion; @@ -91,7 +100,6 @@ extern const QSet NON_VERIFIED_PACKETS; extern const QSet NON_SOURCED_PACKETS; extern const QSet RELIABLE_PACKETS; -QString nameForPacketType(PacketType packetType); PacketVersion versionForPacketType(PacketType packetType); uint qHash(const PacketType& key, uint seed); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index cceb3ba706..fe92fe7745 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1890,8 +1890,8 @@ bool Octree::readSVOFromStream(unsigned long streamLength, QDataStream& inputStr versionForPacketType(expectedDataPacketType()), gotVersion); } } else { - qCDebug(octree) << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType) - << " Got: " << nameForPacketType(gotType); + qCDebug(octree) << "SVO file type mismatch. Expected: " << expectedType + << " Got: " << gotType; } } else { From 8bdb81d832723a944fd7dea220bc0f755950be2e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 15:03:19 -0800 Subject: [PATCH 0897/1003] send disconnect packet from node when leaving domain --- libraries/networking/src/DomainHandler.cpp | 22 +++++++++++++++++-- libraries/networking/src/DomainHandler.h | 3 ++- .../networking/src/udt/PacketHeaders.cpp | 10 +++++---- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index df024b361d..1a80f81bbe 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -46,7 +46,13 @@ DomainHandler::DomainHandler(QObject* parent) : connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer); } -void DomainHandler::clearConnectionInfo() { +void DomainHandler::disconnect() { + // if we're currently connected to a domain, send a disconnect packet on our way out + if (_isConnected) { + sendDisconnectPacket(); + } + + // clear member variables that hold the connection state to a domain _uuid = QUuid(); _connectionToken = QUuid(); @@ -60,6 +66,18 @@ void DomainHandler::clearConnectionInfo() { setIsConnected(false); } +void DomainHandler::sendDisconnectPacket() { + // The DomainDisconnect packet is not verified - we're relying on the eventual addition of DTLS to the + // domain-server connection to stop greifing here + + // construct the disconnect packet once (an empty packet but sourced with our current session UUID) + static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnect, 0); + + // send the disconnect packet to the current domain server + auto nodeList = DependencyManager::get(); + nodeList->sendUnreliablePacket(*disconnectPacket, _sockAddr); +} + void DomainHandler::clearSettings() { _settingsObject = QJsonObject(); _failedSettingsRequests = 0; @@ -67,7 +85,7 @@ void DomainHandler::clearSettings() { void DomainHandler::softReset() { qCDebug(networking) << "Resetting current domain connection information."; - clearConnectionInfo(); + disconnect(); clearSettings(); } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 9dd4254c30..da22c4527d 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -35,7 +35,6 @@ class DomainHandler : public QObject { public: DomainHandler(QObject* parent = 0); - void clearConnectionInfo(); void clearSettings(); const QUuid& getUUID() const { return _uuid; } @@ -113,6 +112,8 @@ signals: void settingsReceiveFail(); private: + void disconnect(); + void sendDisconnectPacket(); void hardReset(); QUuid _uuid; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index b389d4d3df..95df09fb5c 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -19,7 +19,8 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketType::NodeJsonStats << PacketType::EntityQuery << PacketType::OctreeDataNack << PacketType::EntityEditNack - << PacketType::DomainListRequest << PacketType::StopNode; + << PacketType::DomainListRequest << PacketType::StopNode + << PacketType::DomainDisconnect; const QSet NON_SOURCED_PACKETS = QSet() << PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment @@ -30,7 +31,8 @@ const QSet NON_SOURCED_PACKETS = QSet() << PacketType::DomainSettingsRequest << PacketType::DomainSettings << PacketType::ICEServerPeerInformation << PacketType::ICEServerQuery << PacketType::ICEServerHeartbeat << PacketType::ICEPing << PacketType::ICEPingReply - << PacketType::AssignmentClientStatus << PacketType::StopNode; + << PacketType::AssignmentClientStatus << PacketType::StopNode + << PacketType::DomainServerRemovedNode; const QSet RELIABLE_PACKETS = QSet(); @@ -48,8 +50,8 @@ PacketVersion versionForPacketType(PacketType packetType) { } uint qHash(const PacketType& key, uint seed) { - // seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch to - // strongly typed enum for PacketType + // seems odd that Qt couldn't figure out this cast itself, but this fixes a compile error after switch + // to strongly typed enum for PacketType return qHash((quint8) key, seed); } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 147c001ba9..4b5a6b5f52 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -86,7 +86,8 @@ public: AssetUploadReply, AssetGetInfo, AssetGetInfoReply, - NodeDisconnect + DomainDisconnect, + DomainServerRemovedNode }; }; From d7be1699a7f0d5e13e735029f2b9996daf69194f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 15:27:10 -0800 Subject: [PATCH 0898/1003] adjust how the decision to send action changes over the wire is made --- interface/src/avatar/AvatarActionHold.cpp | 3 ++- libraries/entities/src/EntityItem.cpp | 8 +++++--- libraries/entities/src/EntityItem.h | 14 ++++++++++---- libraries/physics/src/EntityMotionState.cpp | 3 ++- libraries/physics/src/ObjectActionOffset.cpp | 3 ++- libraries/physics/src/ObjectActionSpring.cpp | 3 ++- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 0d86e79a2d..839f2d4fbb 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -211,7 +211,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataNeedsUpdate(true); + ownerEntity->setActionDataDirty(true); + ownerEntity->setActionDataNeedsTransmit(true); } }); activateBody(); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index ef39cf8063..807186a304 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1740,6 +1740,8 @@ void EntityItem::deserializeActionsInternal() { } } + _actionDataDirty = true; + return; } @@ -1800,11 +1802,11 @@ void EntityItem::serializeActions(bool& success, QByteArray& result) const { } const QByteArray EntityItem::getActionDataInternal() const { - if (_actionDataNeedsUpdate) { + if (_actionDataDirty) { bool success; serializeActions(success, _allActionsDataCache); if (success) { - _actionDataNeedsUpdate = false; + _actionDataDirty = false; } } return _allActionsDataCache; @@ -1814,7 +1816,7 @@ const QByteArray EntityItem::getActionData() const { QByteArray result; assertUnlocked(); - if (_actionDataNeedsUpdate) { + if (_actionDataDirty) { withWriteLock([&] { getActionDataInternal(); result = _allActionsDataCache; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 6e4f3ac2ca..5b47198e97 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -408,8 +408,13 @@ public: QList getActionIDs() { return _objectActions.keys(); } QVariantMap getActionArguments(const QUuid& actionID) const; void deserializeActions(); - void setActionDataNeedsUpdate(bool value) const { _actionDataNeedsUpdate = value; } - bool actionDataNeedsUpdate() const { return _actionDataNeedsUpdate; } + + void setActionDataDirty(bool value) const { _actionDataDirty = value; } + bool actionDataDirty() const { return _actionDataDirty; } + + void setActionDataNeedsTransmit(bool value) const { _actionDataNeedsTransmit = value; } + bool actionDataNeedsTransmit() const { return _actionDataNeedsTransmit; } + bool shouldSuppressLocationEdits() const; void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; } @@ -442,7 +447,7 @@ protected: mutable bool _recalcAABox = true; mutable bool _recalcMinAACube = true; mutable bool _recalcMaxAACube = true; - + float _glowLevel; float _localRenderAlpha; float _density = ENTITY_ITEM_DEFAULT_DENSITY; // kg/m^3 @@ -512,7 +517,8 @@ protected: // are used to keep track of and work around this situation. void checkWaitingToRemove(EntitySimulation* simulation = nullptr); mutable QSet _actionsToRemove; - mutable bool _actionDataNeedsUpdate = false; + mutable bool _actionDataDirty = false; + mutable bool _actionDataNeedsTransmit = false; // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 841a41cb04..03e1c1c5b7 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -297,8 +297,9 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverPosition += dt * _serverVelocity; } - if (_entity->actionDataNeedsUpdate()) { + if (_entity->actionDataNeedsTransmit()) { setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); + _entity->setActionDataNeedsTransmit(false); return true; } diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index be2f5b40a0..5c2999a0a1 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -129,7 +129,8 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataNeedsUpdate(true); + ownerEntity->setActionDataDirty(true); + ownerEntity->setActionDataNeedsTransmit(true); } }); activateBody(); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index 7289c88a8f..d9d6a323a7 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -162,7 +162,8 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { auto ownerEntity = _ownerEntity.lock(); if (ownerEntity) { - ownerEntity->setActionDataNeedsUpdate(true); + ownerEntity->setActionDataDirty(true); + ownerEntity->setActionDataNeedsTransmit(true); } }); activateBody(); From 1c9396d66e49ac392fc02ea72137f37bd974f93b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 15:43:03 -0800 Subject: [PATCH 0899/1003] handle disconnect request in domain-server --- domain-server/src/DomainServer.cpp | 10 ++++++++++ domain-server/src/DomainServer.h | 3 ++- libraries/networking/src/DomainHandler.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b5fd9f2b20..b0aacd23f7 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -272,6 +272,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket"); packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); packetReceiver.registerMessageListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); + packetReceiver.registerListener(PacketType::DomainDisconnectRequest, this, "processNodeDisconnectRequestPacket"); // NodeList won't be available to the settings manager when it is created, so call registerListener here packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket"); @@ -1826,3 +1827,12 @@ void DomainServer::processPathQueryPacket(QSharedPointer packet) { } } } + +void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer packet) { + // This packet has been matched to a source node and they're asking not to be in the domain anymore + auto limitedNodeList = DependencyManager::get(); + + qDebug() << "Received a disconnect request from node with UUID" << packet->getSourceID(); + + limitedNodeList->killNodeWithUUID(packet->getSourceID()); +} diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index df42bf3ad9..e5b3d3b3fd 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -60,7 +60,8 @@ public slots: void processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processNodeJSONStatsPacket(QSharedPointer packetList, SharedNodePointer sendingNode); void processPathQueryPacket(QSharedPointer packet); - + void processNodeDisconnectRequestPacket(QSharedPointer packet); + private slots: void aboutToQuit(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 1a80f81bbe..f7d26f25c5 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -71,7 +71,7 @@ void DomainHandler::sendDisconnectPacket() { // domain-server connection to stop greifing here // construct the disconnect packet once (an empty packet but sourced with our current session UUID) - static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnect, 0); + static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnectRequest, 0); // send the disconnect packet to the current domain server auto nodeList = DependencyManager::get(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 95df09fb5c..f5c66617a8 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -20,7 +20,7 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketType::NodeJsonStats << PacketType::EntityQuery << PacketType::OctreeDataNack << PacketType::EntityEditNack << PacketType::DomainListRequest << PacketType::StopNode - << PacketType::DomainDisconnect; + << PacketType::DomainDisconnectRequest; const QSet NON_SOURCED_PACKETS = QSet() << PacketType::StunResponse << PacketType::CreateAssignment << PacketType::RequestAssignment diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 4b5a6b5f52..da061d8fdf 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -86,7 +86,7 @@ public: AssetUploadReply, AssetGetInfo, AssetGetInfoReply, - DomainDisconnect, + DomainDisconnectRequest, DomainServerRemovedNode }; }; From 6f83f32eccbc2726e9153c3bd3ea8f2e2d19b2ab Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 16:27:54 -0800 Subject: [PATCH 0900/1003] adjust how the decision to send action changes over the wire is made --- libraries/physics/src/EntityMotionState.cpp | 43 ++++++++------------- libraries/physics/src/EntityMotionState.h | 1 - 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 03e1c1c5b7..66f5d46c18 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -87,15 +87,11 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { } _serverActionData = _entity->getActionData(); - if (!_serverShouldSuppressLocationEdits) { - _serverPosition = _entity->getPosition(); - _serverRotation = _entity->getRotation(); - _serverVelocity = _entity->getVelocity(); - _serverAngularVelocity = _entity->getAngularVelocity(); - _serverAcceleration = _entity->getAcceleration(); - } - - _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); + _serverPosition = _entity->getPosition(); + _serverRotation = _entity->getRotation(); + _serverVelocity = _entity->getVelocity(); + _serverAngularVelocity = _entity->getAngularVelocity(); + _serverAcceleration = _entity->getAcceleration(); } // virtual @@ -262,7 +258,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); - _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); _sentInactive = true; return false; } @@ -448,24 +443,20 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q EntityItemProperties properties; + // remember properties for local server prediction + _serverPosition = _entity->getPosition(); + _serverRotation = _entity->getRotation(); + _serverVelocity = _entity->getVelocity(); + _serverAcceleration = _entity->getAcceleration(); + _serverAngularVelocity = _entity->getAngularVelocity(); + _serverActionData = _entity->getActionData(); // explicitly set the properties that changed so that they will be packed - if (!_serverShouldSuppressLocationEdits) { - // remember properties for local server prediction - _serverPosition = _entity->getPosition(); - _serverRotation = _entity->getRotation(); - _serverVelocity = _entity->getVelocity(); - _serverAcceleration = _entity->getAcceleration(); - _serverAngularVelocity = _entity->getAngularVelocity(); - - properties.setPosition(_serverPosition); - properties.setRotation(_serverRotation); - properties.setVelocity(_serverVelocity); - properties.setAcceleration(_serverAcceleration); - properties.setAngularVelocity(_serverAngularVelocity); - } - _serverShouldSuppressLocationEdits = _entity->shouldSuppressLocationEdits(); - _serverActionData = _entity->getActionData(); + properties.setPosition(_serverPosition); + properties.setRotation(_serverRotation); + properties.setVelocity(_serverVelocity); + properties.setAcceleration(_serverAcceleration); + properties.setAngularVelocity(_serverAngularVelocity); properties.setActionData(_serverActionData); // set the LastEdited of the properties but NOT the entity itself diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index f70eff44a4..188e7096b9 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -110,7 +110,6 @@ protected: glm::vec3 _serverGravity; glm::vec3 _serverAcceleration; QByteArray _serverActionData; - bool _serverShouldSuppressLocationEdits = false; uint32_t _lastMeasureStep; glm::vec3 _lastVelocity; From 83e7eb58b9a0877450ac1fd867e94f11cb4ddd09 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 16:36:06 -0800 Subject: [PATCH 0901/1003] add special packets and nack packets to outbound stats --- .../src/octree/OctreeInboundPacketProcessor.cpp | 6 +++++- assignment-client/src/octree/OctreeSendThread.cpp | 12 +++++++++++- assignment-client/src/octree/OctreeSendThread.h | 3 +++ assignment-client/src/octree/OctreeServer.cpp | 10 ++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 92c08152f7..0cdc7f9921 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -240,6 +240,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() { auto nodeList = DependencyManager::get(); int packetsSent = 0; + int totalBytesSent = 0; NodeToSenderStatsMapIterator i = _singleSenderStats.begin(); while (i != _singleSenderStats.end()) { @@ -291,12 +292,15 @@ int OctreeInboundPacketProcessor::sendNackPackets() { packetsSent += nackPacketList->getNumPackets(); // send the list of nack packets - nodeList->sendPacketList(std::move(nackPacketList), *destinationNode); + totalBytesSent += nodeList->sendPacketList(std::move(nackPacketList), *destinationNode); } ++i; } + OctreeSendThread::_totalPackets += packetsSent; + OctreeSendThread::_totalBytes += totalBytesSent; + return packetsSent; } diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 1906cb7979..1d92ed917f 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -118,6 +118,10 @@ AtomicUIntStat OctreeSendThread::_totalBytes { 0 }; AtomicUIntStat OctreeSendThread::_totalWastedBytes { 0 }; AtomicUIntStat OctreeSendThread::_totalPackets { 0 }; +AtomicUIntStat OctreeSendThread::_totalSpecialBytes { 0 }; +AtomicUIntStat OctreeSendThread::_totalSpecialPackets { 0 }; + + int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { OctreeServer::didHandlePacketSend(this); @@ -580,11 +584,17 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // send the environment packet // TODO: should we turn this into a while loop to better handle sending multiple special packets if (_myServer->hasSpecialPacketsToSend(_node) && !nodeData->isShuttingDown()) { - int specialPacketsSent; + int specialPacketsSent = 0; trueBytesSent += _myServer->sendSpecialPackets(_node, nodeData, specialPacketsSent); nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed truePacketsSent += specialPacketsSent; packetsSentThisInterval += specialPacketsSent; + + _totalPackets += specialPacketsSent; + _totalBytes += trueBytesSent; + + _totalSpecialPackets += specialPacketsSent; + _totalSpecialBytes += trueBytesSent; } // Re-send packets that were nacked by the client diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 69ea0be567..6e640942e7 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -38,6 +38,9 @@ public: static AtomicUIntStat _totalWastedBytes; static AtomicUIntStat _totalPackets; + static AtomicUIntStat _totalSpecialBytes; + static AtomicUIntStat _totalSpecialPackets; + static AtomicUIntStat _usleepTime; static AtomicUIntStat _usleepCalls; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 818d54ee97..ad3df11474 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -415,6 +415,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks(); quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor(); + quint64 totalOutboundSpecialPackets = OctreeSendThread::_totalSpecialPackets; + quint64 totalOutboundSpecialBytes = OctreeSendThread::_totalSpecialBytes; + statsString += QString(" Total Clients Connected: %1 clients\r\n") .arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' ')); @@ -606,6 +609,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url .arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Total Outbound Bytes: %1 bytes\r\n") .arg(locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' ')); + + statsString += QString(" Total Outbound Special Packets: %1 packets\r\n") + .arg(locale.toString((uint)totalOutboundSpecialPackets).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Total Outbound Special Bytes: %1 bytes\r\n") + .arg(locale.toString((uint)totalOutboundSpecialBytes).rightJustified(COLUMN_WIDTH, ' ')); + + statsString += QString(" Total Wasted Bytes: %1 bytes\r\n") .arg(locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString().sprintf(" Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n", From 756b382a75981593971073257de5d89233e0219c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 17:12:20 -0800 Subject: [PATCH 0902/1003] adjust how the decision to send action changes over the wire is made --- libraries/physics/src/EntityMotionState.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 66f5d46c18..877ea168aa 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -86,12 +86,12 @@ void EntityMotionState::updateServerPhysicsVariables(const QUuid& sessionID) { return; } - _serverActionData = _entity->getActionData(); _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); _serverVelocity = _entity->getVelocity(); _serverAngularVelocity = _entity->getAngularVelocity(); _serverAcceleration = _entity->getAcceleration(); + _serverActionData = _entity->getActionData(); } // virtual @@ -298,10 +298,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } - if (_entity->shouldSuppressLocationEdits()) { - return false; - } - // Else we measure the error between current and extrapolated transform (according to expected behavior // of remote EntitySimulation) and return true if the error is significant. @@ -441,8 +437,6 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _sentInactive = false; } - EntityItemProperties properties; - // remember properties for local server prediction _serverPosition = _entity->getPosition(); _serverRotation = _entity->getRotation(); @@ -451,6 +445,8 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q _serverAngularVelocity = _entity->getAngularVelocity(); _serverActionData = _entity->getActionData(); + EntityItemProperties properties; + // explicitly set the properties that changed so that they will be packed properties.setPosition(_serverPosition); properties.setRotation(_serverRotation); From e52e9be44ce26dedd5a3b380f3294f3c11004ba9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 17:23:39 -0800 Subject: [PATCH 0903/1003] re-broadcast disconnects from domain-server --- domain-server/src/DomainServer.cpp | 18 ++++++++++++++++-- libraries/networking/src/DomainHandler.h | 2 +- .../networking/src/ThreadedAssignment.cpp | 9 +++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b0aacd23f7..790548c5b3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1832,7 +1832,21 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer p // This packet has been matched to a source node and they're asking not to be in the domain anymore auto limitedNodeList = DependencyManager::get(); - qDebug() << "Received a disconnect request from node with UUID" << packet->getSourceID(); + const QUuid& nodeUUID = packet->getSourceID(); - limitedNodeList->killNodeWithUUID(packet->getSourceID()); + qDebug() << "Received a disconnect request from node with UUID" << nodeUUID; + + if (limitedNodeList->nodeWithUUID(nodeUUID)) { + limitedNodeList->killNodeWithUUID(nodeUUID); + + static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID); + + removedNodePacket->reset(); + removedNodePacket->write(nodeUUID.toRfc4122()); + + // broadcast out the DomainServerRemovedNode message + limitedNodeList->eachNode([&limitedNodeList](const SharedNodePointer& otherNode){ + limitedNodeList->sendUnreliablePacket(*removedNodePacket, *otherNode); + }); + } } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index da22c4527d..49bab6dc28 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -35,6 +35,7 @@ class DomainHandler : public QObject { public: DomainHandler(QObject* parent = 0); + void disconnect(); void clearSettings(); const QUuid& getUUID() const { return _uuid; } @@ -112,7 +113,6 @@ signals: void settingsReceiveFail(); private: - void disconnect(); void sendDisconnectPacket(); void hardReset(); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 0422c03297..6855c2eec3 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -33,14 +33,19 @@ void ThreadedAssignment::setFinished(bool isFinished) { if (_isFinished) { qDebug() << "ThreadedAssignment::setFinished(true) called - finishing up."; - - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + + auto nodeList = DependencyManager::get(); + + auto& packetReceiver = nodeList->getPacketReceiver(); // we should de-register immediately for any of our packets packetReceiver.unregisterListener(this); // we should also tell the packet receiver to drop packets while we're cleaning up packetReceiver.setShouldDropPackets(true); + + // send a disconnect packet to the domain + nodeList->getDomainHandler().disconnect(); if (_domainServerTimer) { // stop the domain-server check in timer by calling deleteLater so it gets cleaned up on NL thread From ed8a604e136dd257f090d34428cd7b92d04da7a5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 17:36:45 -0800 Subject: [PATCH 0904/1003] adjust how the decision to send action changes over the wire is made --- libraries/physics/src/EntityMotionState.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 877ea168aa..24957bbf30 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -244,7 +244,7 @@ bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const { return false; } assert(entityTreeIsLocked()); - return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID(); + return _outgoingPriority != NO_PRORITY || sessionID == _entity->getSimulatorID() || _entity->actionDataNeedsTransmit(); } bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { @@ -298,6 +298,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { return true; } + if (_entity->shouldSuppressLocationEdits()) { + return false; + } + // Else we measure the error between current and extrapolated transform (according to expected behavior // of remote EntitySimulation) and return true if the error is significant. From 6b2987eef8c3abb3c44952c8a6d739e0c866cf5c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 17:47:51 -0800 Subject: [PATCH 0905/1003] handle domain server node removal in NodeList --- interface/src/Application.cpp | 10 +++++++++- libraries/networking/src/NodeList.cpp | 8 ++++++++ libraries/networking/src/NodeList.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7a564bbbf0..2679d4b5ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -804,8 +804,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : void Application::aboutToQuit() { emit beforeAboutToQuit(); + getActiveDisplayPlugin()->deactivate(); + _aboutToQuit = true; + cleanupBeforeQuit(); } @@ -831,8 +834,13 @@ void Application::cleanupBeforeQuit() { _entities.clear(); // this will allow entity scripts to properly shutdown + auto nodeList = DependencyManager::get(); + + // send the domain a disconnect packet + nodeList->getDomainHandler().disconnect(); + // tell the packet receiver we're shutting down, so it can drop packets - DependencyManager::get()->getPacketReceiver().setShouldDropPackets(true); + nodeList->getPacketReceiver().setShouldDropPackets(true); _entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts ScriptEngine::stopAllScripts(this); // stop all currently running global scripts diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b262904c63..19ed8073c1 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -103,6 +103,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket"); packetReceiver.registerListener(PacketType::DomainServerPathResponse, this, "processDomainServerPathResponse"); + packetReceiver.registerListener(PacketType::DomainServerRemovedNode, this, "processDomainServerRemovedNode"); } qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) { @@ -513,6 +514,13 @@ void NodeList::processDomainServerAddedNode(QSharedPointer packet) { parseNodeFromPacketStream(packetStream); } +void NodeList::processDomainServerRemovedNode(QSharedPointer packet) { + // read the UUID from the packet, remove it if it exists + QUuid nodeUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + qDebug() << "Received packet from domain-server to remove node with UUID" << uuidStringWithoutCurlyBraces(nodeUUID); + killNodeWithUUID(nodeUUID); +} + void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) { // setup variables to read into from QDataStream qint8 nodeType; diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 3aae3e3dfc..880f9b05f9 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -74,6 +74,7 @@ public slots: void processDomainServerList(QSharedPointer packet); void processDomainServerAddedNode(QSharedPointer packet); + void processDomainServerRemovedNode(QSharedPointer packet); void processDomainServerPathResponse(QSharedPointer packet); void processDomainServerConnectionTokenPacket(QSharedPointer packet); From 1fd37b51a2bced38a435128d327e0aa4269388b7 Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 12 Nov 2015 17:54:35 -0800 Subject: [PATCH 0906/1003] trying to get somewhere.... --- examples/utilities/record/recorder.js | 2 + interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 67 +++++++++++++++++++-------- libraries/avatars/src/AvatarData.h | 3 ++ 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/examples/utilities/record/recorder.js b/examples/utilities/record/recorder.js index 6d49030a28..13ea1e0c72 100644 --- a/examples/utilities/record/recorder.js +++ b/examples/utilities/record/recorder.js @@ -176,6 +176,8 @@ function formatTime(time) { var SEC_PER_MIN = 60; var MSEC_PER_SEC = 1000; + time = time * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR); + var hours = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR)); time -= hours * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5e14c66ff1..4d62946a5f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -608,7 +608,7 @@ float MyAvatar::recorderElapsed() { if (!_recorder) { return 0; } - return (float)_recorder->position() / MSECS_PER_SECOND; + return (float)_recorder->position(); } QMetaObject::Connection _audioClientRecorderConnection; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index adf471c50e..e2d2c05ef6 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -804,12 +804,12 @@ float AvatarData::playerElapsed() { return 0; } if (QThread::currentThread() != thread()) { - qint64 result; + float result; QMetaObject::invokeMethod(this, "playerElapsed", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(qint64, result)); + Q_RETURN_ARG(float, result)); return result; } - return (float)_player->position() / MSECS_PER_SECOND; + return (float)_player->position(); } float AvatarData::playerLength() { @@ -817,12 +817,12 @@ float AvatarData::playerLength() { return 0; } if (QThread::currentThread() != thread()) { - qint64 result; + float result; QMetaObject::invokeMethod(this, "playerLength", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(qint64, result)); + Q_RETURN_ARG(float, result)); return result; } - return _player->length() / MSECS_PER_SECOND; + return _player->length(); } void AvatarData::loadRecording(const QString& filename) { @@ -870,7 +870,7 @@ void AvatarData::setPlayerTime(float time) { return; } - _player->seek(time * MSECS_PER_SECOND); + _player->seek(time); } void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) { @@ -1532,7 +1532,7 @@ Transform AvatarData::getTransform() const { static const QString JSON_AVATAR_BASIS = QStringLiteral("basisTransform"); static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform"); -static const QString JSON_AVATAR_JOINT_ROTATIONS = QStringLiteral("jointRotations"); +static const QString JSON_AVATAR_JOINT_ARRAY = QStringLiteral("jointArray"); static const QString JSON_AVATAR_HEAD = QStringLiteral("head"); static const QString JSON_AVATAR_HEAD_ROTATION = QStringLiteral("rotation"); static const QString JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS = QStringLiteral("blendShapes"); @@ -1544,6 +1544,24 @@ static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments"); +QJsonValue toJsonValue(const JointData& joint) { + QJsonArray result; + result.push_back(toJsonValue(joint.rotation)); + result.push_back(toJsonValue(joint.translation)); + return result; +} + +JointData jointDataFromJsonValue(const QJsonValue& json) { + JointData result; + if (json.isArray()) { + QJsonArray array = json.toArray(); + result.rotation = quatFromJsonValue(array[0]); + result.rotationSet = true; + result.translation = vec3FromJsonValue(array[1]); + result.translationSet = false; + } + return result; +} // Every frame will store both a basis for the recording and a relative transform // This allows the application to decide whether playback should be relative to an avatar's @@ -1575,13 +1593,16 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) { root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); } + } else { + root[JSON_AVATAR_RELATIVE] = Transform::toJson(_avatar->getTransform()); } - QJsonArray jointRotations; - for (const auto& jointRotation : _avatar->getJointRotations()) { - jointRotations.push_back(toJsonValue(jointRotation)); + // Skeleton pose + QJsonArray jointArray; + for (const auto& joint : _avatar->getRawJointData()) { + jointArray.push_back(toJsonValue(joint)); } - root[JSON_AVATAR_JOINT_ROTATIONS] = jointRotations; + root[JSON_AVATAR_JOINT_ARRAY] = jointArray; const HeadData* head = _avatar->getHeadData(); if (head) { @@ -1646,21 +1667,29 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { _avatar->setTargetScale(worldTransform.getScale().x); } -#if 0 + if (root.contains(JSON_AVATAR_ATTACHEMENTS)) { // FIXME de-serialize attachment data } // Joint rotations are relative to the avatar, so they require no basis correction - if (root.contains(JSON_AVATAR_JOINT_ROTATIONS)) { - QVector jointRotations; - QJsonArray jointRotationsJson = root[JSON_AVATAR_JOINT_ROTATIONS].toArray(); - jointRotations.reserve(jointRotationsJson.size()); - for (const auto& jointRotationJson : jointRotationsJson) { - jointRotations.push_back(quatFromJsonValue(jointRotationJson)); + if (root.contains(JSON_AVATAR_JOINT_ARRAY)) { + QVector jointArray; + QJsonArray jointArrayJson = root[JSON_AVATAR_JOINT_ARRAY].toArray(); + jointArray.reserve(jointArrayJson.size()); + for (const auto& jointJson : jointArrayJson) { + jointArray.push_back(jointDataFromJsonValue(jointJson)); } + + QVector jointRotations; + jointRotations.reserve(jointArray.size()); + for (const auto& joint : jointArray) { + jointRotations.push_back(joint.rotation); + } + _avatar->setJointRotations(jointRotations); } +#if 0 // Most head data is relative to the avatar, and needs no basis correction, // but the lookat vector does need correction HeadData* head = _avatar->_headData; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 7716a16a1c..26bc9d83ff 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -457,6 +457,9 @@ public: bool translationSet = false; }; +QJsonValue toJsonValue(const JointData& joint); +JointData jointDataFromJsonValue(const QJsonValue& q); + class AttachmentData { public: QUrl modelURL; From d932ba74fd75ef2244a165ad76c5e182fd9d4d67 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 17:58:12 -0800 Subject: [PATCH 0907/1003] remove the avatar kill packet from Interface --- interface/src/Application.cpp | 5 +---- interface/src/avatar/MyAvatar.cpp | 5 ----- interface/src/avatar/MyAvatar.h | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2679d4b5ec..f8d958c82e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -836,7 +836,7 @@ void Application::cleanupBeforeQuit() { auto nodeList = DependencyManager::get(); - // send the domain a disconnect packet + // send the domain a disconnect packet, force a clear of the IP so we can't nodeList->getDomainHandler().disconnect(); // tell the packet receiver we're shutting down, so it can drop packets @@ -860,9 +860,6 @@ void Application::cleanupBeforeQuit() { saveSettings(); _window->saveGeometry(); - // let the avatar mixer know we're out - MyAvatar::sendKillAvatar(); - // stop the AudioClient QMetaObject::invokeMethod(DependencyManager::get().data(), "stop", Qt::BlockingQueuedConnection); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 852e1d1389..a59b26b51a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1024,11 +1024,6 @@ int MyAvatar::parseDataFromBuffer(const QByteArray& buffer) { return buffer.size(); } -void MyAvatar::sendKillAvatar() { - auto killPacket = NLPacket::create(PacketType::KillAvatar, 0); - DependencyManager::get()->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::AvatarMixer); -} - void MyAvatar::updateLookAtTargetAvatar() { // // Look at the avatar whose eyes are closest to the ray in direction of my avatar's head diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 52f1ffce3f..bb5fd0cf0a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -162,8 +162,6 @@ public: eyeContactTarget getEyeContactTarget(); - static void sendKillAvatar(); - Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; } Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } From ea38c4cc49e90b98f9ccf92d9634a69658b5ebb6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 12 Nov 2015 18:04:15 -0800 Subject: [PATCH 0908/1003] don't allow domain check-ins while shutting down --- interface/src/Application.cpp | 3 ++- libraries/networking/src/NodeList.cpp | 4 ++++ libraries/networking/src/NodeList.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f8d958c82e..37ea5405da 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -836,8 +836,9 @@ void Application::cleanupBeforeQuit() { auto nodeList = DependencyManager::get(); - // send the domain a disconnect packet, force a clear of the IP so we can't + // send the domain a disconnect packet, force stoppage of domain-server check-ins nodeList->getDomainHandler().disconnect(); + nodeList->setIsShuttingDown(true); // tell the packet receiver we're shutting down, so it can drop packets nodeList->getPacketReceiver().setShouldDropPackets(true); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 19ed8073c1..e03ac47854 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -219,6 +219,10 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) } void NodeList::sendDomainServerCheckIn() { + if (_isShuttingDown) { + qCDebug(networking) << "Refusing to send a domain-server check in while shutting down."; + } + if (_publicSockAddr.isNull()) { // we don't know our public socket and we need to send it to the domain server qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in."; diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 880f9b05f9..5b9a4e5ae5 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -66,6 +66,8 @@ public: void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); + + void setIsShuttingDown(bool isShuttingDown) { _isShuttingDown = isShuttingDown; } public slots: void reset(); @@ -115,6 +117,7 @@ private: DomainHandler _domainHandler; int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; + bool _isShuttingDown { false }; }; #endif // hifi_NodeList_h From ecaa50c0ff82de10c2714b26cf0fac8c1a51cb9b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 18:25:18 -0800 Subject: [PATCH 0909/1003] fix for infinite loop in erase entities special packet --- .../src/entities/EntityServer.cpp | 114 ++++++++++++++++-- libraries/entities/src/EntityTree.cpp | 89 +------------- libraries/entities/src/EntityTree.h | 17 ++- 3 files changed, 125 insertions(+), 95 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 493a16fea4..4448a3c5c3 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -101,24 +101,121 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN EntityNodeData* nodeData = static_cast(node->getLinkedData()); if (nodeData) { + quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt(); + quint64 considerEntitiesSince = EntityTree::getAdjustedConsiderSince(deletedEntitiesSentAt); + quint64 deletePacketSentAt = usecTimestampNow(); EntityTreePointer tree = std::static_pointer_cast(_tree); + auto recentlyDeleted = tree->getRecentlyDeletedEntityIDs(); bool hasMoreToSend = true; packetsSent = 0; - while (hasMoreToSend) { - auto specialPacket = tree->encodeEntitiesDeletedSince(queryNode->getSequenceNumber(), deletedEntitiesSentAt, - hasMoreToSend); + // create a new special packet + std::unique_ptr deletesPacket = NLPacket::create(PacketType::EntityErase); - queryNode->packetSent(*specialPacket); + // pack in flags + OCTREE_PACKET_FLAGS flags = 0; + deletesPacket->writePrimitive(flags); - totalBytes += specialPacket->getDataSize(); - packetsSent++; + // pack in sequence number + auto sequenceNumber = queryNode->getSequenceNumber(); + deletesPacket->writePrimitive(sequenceNumber); - DependencyManager::get()->sendPacket(std::move(specialPacket), *node); - } + // pack in timestamp + OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); + deletesPacket->writePrimitive(now); + + // figure out where we are now and pack a temporary number of IDs + uint16_t numberOfIDs = 0; + qint64 numberOfIDsPos = deletesPacket->pos(); + deletesPacket->writePrimitive(numberOfIDs); + + // we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been + // deleted since we last sent to this node + auto it = recentlyDeleted.constBegin(); + while (it != recentlyDeleted.constEnd()) { + + // if the timestamp is more recent then out last sent time, include it + if (it.key() > considerEntitiesSince) { + + // get all the IDs for this timestamp + const auto& entityIDsFromTime = recentlyDeleted.values(it.key()); + + for (const auto& entityID : entityIDsFromTime) { + + // check to make sure we have room for one more ID, if we don't have more + // room, then send out this packet and create another one + if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) { + + // replace the count for the number of included IDs + deletesPacket->seek(numberOfIDsPos); + deletesPacket->writePrimitive(numberOfIDs); + + // Send the current packet + queryNode->packetSent(*deletesPacket); + auto thisPacketSize = deletesPacket->getDataSize(); + totalBytes += thisPacketSize; + packetsSent++; + DependencyManager::get()->sendPacket(std::move(deletesPacket), *node); + + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << "EntityServer::sendSpecialPackets() sending packet packetsSent[" << packetsSent << "] size:" << thisPacketSize; + #endif + + + // create another packet + deletesPacket = NLPacket::create(PacketType::EntityErase); + + // pack in flags + deletesPacket->writePrimitive(flags); + + // pack in sequence number + sequenceNumber = queryNode->getSequenceNumber(); + deletesPacket->writePrimitive(sequenceNumber); + + // pack in timestamp + deletesPacket->writePrimitive(now); + + // figure out where we are now and pack a temporary number of IDs + numberOfIDs = 0; + numberOfIDsPos = deletesPacket->pos(); + deletesPacket->writePrimitive(numberOfIDs); + } + + // FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server + // to the client. These were causing "lost" entities like flashlights and laser pointers + // now that we keep around some additional history of the erased entities and resend that + // history for a longer time window, these entities are not "lost". But we haven't yet + // found/fixed the underlying issue that caused bad UUIDs to be sent to some users. + deletesPacket->write(entityID.toRfc4122()); + ++numberOfIDs; + + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID; + #endif + } // end for (ids) + + } // end if (it.val > sinceLast) + + + ++it; + } // end while + + // replace the count for the number of included IDs + deletesPacket->seek(numberOfIDsPos); + deletesPacket->writePrimitive(numberOfIDs); + + // Send the current packet + queryNode->packetSent(*deletesPacket); + auto thisPacketSize = deletesPacket->getDataSize(); + totalBytes += thisPacketSize; + packetsSent++; + DependencyManager::get()->sendPacket(std::move(deletesPacket), *node); + #ifdef EXTRA_ERASE_DEBUGGING + qDebug() << "EntityServer::sendSpecialPackets() sending packet packetsSent[" << packetsSent << "] size:" << thisPacketSize; + #endif nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt); } @@ -134,6 +231,7 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN return totalBytes; } + void EntityServer::pruneDeletedEntities() { EntityTreePointer tree = std::static_pointer_cast(_tree); if (tree->hasAnyDeletedEntities()) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 0174dbe39b..c2f635c95f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -801,8 +801,13 @@ void EntityTree::update() { } } +quint64 EntityTree::getAdjustedConsiderSince(quint64 sinceTime) { + return (sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER); +} + + bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { - quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; + quint64 considerEntitiesSince = getAdjustedConsiderSince(sinceTime); // we can probably leverage the ordered nature of QMultiMap to do this quickly... bool hasSomethingNewer = false; @@ -829,88 +834,6 @@ bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) { return hasSomethingNewer; } -// sinceTime is an in/out parameter - it will be side effected with the last time sent out -std::unique_ptr EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, - bool& hasMore) { - quint64 considerEntitiesSince = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; - auto deletesPacket = NLPacket::create(PacketType::EntityErase); - - // pack in flags - OCTREE_PACKET_FLAGS flags = 0; - deletesPacket->writePrimitive(flags); - - // pack in sequence number - deletesPacket->writePrimitive(sequenceNumber); - - // pack in timestamp - OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); - deletesPacket->writePrimitive(now); - - // figure out where we are now and pack a temporary number of IDs - uint16_t numberOfIDs = 0; - qint64 numberOfIDsPos = deletesPacket->pos(); - deletesPacket->writePrimitive(numberOfIDs); - - // we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been - // deleted since we last sent to this node - { - QReadLocker locker(&_recentlyDeletedEntitiesLock); - - bool hasFilledPacket = false; - - auto it = _recentlyDeletedEntityItemIDs.constBegin(); - while (it != _recentlyDeletedEntityItemIDs.constEnd()) { - QList values = _recentlyDeletedEntityItemIDs.values(it.key()); - for (int valueItem = 0; valueItem < values.size(); ++valueItem) { - - // if the timestamp is more recent then out last sent time, include it - if (it.key() > considerEntitiesSince) { - QUuid entityID = values.at(valueItem); - - // FIXME - we still seem to see cases where incorrect EntityIDs get sent from the server - // to the client. These were causing "lost" entities like flashlights and laser pointers - // now that we keep around some additional history of the erased entities and resend that - // history for a longer time window, these entities are not "lost". But we haven't yet - // found/fixed the underlying issue that caused bad UUIDs to be sent to some users. - deletesPacket->write(entityID.toRfc4122()); - ++numberOfIDs; - - #ifdef EXTRA_ERASE_DEBUGGING - qDebug() << "EntityTree::encodeEntitiesDeletedSince() including:" << entityID; - #endif - - // check to make sure we have room for one more ID - if (NUM_BYTES_RFC4122_UUID > deletesPacket->bytesAvailableForWrite()) { - hasFilledPacket = true; - break; - } - } - } - - // check to see if we're about to return - if (hasFilledPacket) { - // let our caller know how far we got - sinceTime = it.key(); - break; - } - - ++it; - } - - // if we got to the end, then we're done sending - if (it == _recentlyDeletedEntityItemIDs.constEnd()) { - hasMore = false; - } - } - - // replace the count for the number of included IDs - deletesPacket->seek(numberOfIDsPos); - deletesPacket->writePrimitive(numberOfIDs); - - return deletesPacket; -} - - // called by the server when it knows all nodes have been sent deleted packets void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) { quint64 considerSinceTime = sinceTime - DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index c177840199..645e3f4f76 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -147,10 +147,19 @@ public: void addNewlyCreatedHook(NewlyCreatedEntityHook* hook); void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook); - bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; } + bool hasAnyDeletedEntities() const { + QReadLocker locker(&_recentlyDeletedEntitiesLock); + return _recentlyDeletedEntityItemIDs.size() > 0; + } + bool hasEntitiesDeletedSince(quint64 sinceTime); - std::unique_ptr encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, - bool& hasMore); + static quint64 getAdjustedConsiderSince(quint64 sinceTime); + + QMultiMap getRecentlyDeletedEntityIDs() const { + QReadLocker locker(&_recentlyDeletedEntitiesLock); + return _recentlyDeletedEntityItemIDs; + } + void forgetEntitiesDeletedBefore(quint64 sinceTime); int processEraseMessage(NLPacket& packet, const SharedNodePointer& sourceNode); @@ -243,7 +252,7 @@ private: QReadWriteLock _newlyCreatedHooksLock; QVector _newlyCreatedHooks; - QReadWriteLock _recentlyDeletedEntitiesLock; + mutable QReadWriteLock _recentlyDeletedEntitiesLock; QMultiMap _recentlyDeletedEntityItemIDs; EntityItemFBXService* _fbxService; From b6b5a5b1c2411c4ffac6b89ab54a7eb1ed828c5c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 12 Nov 2015 18:28:08 -0800 Subject: [PATCH 0910/1003] add comment --- assignment-client/src/entities/EntityServer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 4448a3c5c3..5754a9e057 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -96,6 +96,10 @@ bool EntityServer::hasSpecialPacketsToSend(const SharedNodePointer& node) { return shouldSendDeletedEntities; } +// FIXME - most of the old code for this was encapsulated in EntityTree, I liked that design from a data +// hiding and object oriented perspective. But that didn't really allow us to handle the case of lots +// of entities being deleted at the same time. I'd like to look to move this back into EntityTree but +// for now this works and addresses the bug. int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { int totalBytes = 0; From dbaea5770aba054360001819fbbd454f4242c93d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 Nov 2015 18:28:26 -0800 Subject: [PATCH 0911/1003] include action data in properties suppressed by non-simulation ownership --- libraries/entities/src/EntityTree.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 0e2b713b65..7cbdc8b666 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -198,6 +198,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI properties.setVelocityChanged(false); properties.setAngularVelocityChanged(false); properties.setAccelerationChanged(false); + properties.setActionDataChanged(false); if (wantTerseEditLogging()) { qCDebug(entities) << senderNode->getUUID() << "physical edits suppressed"; From 89d120ab3d42ca26f7536961d861b29e009847c2 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 12 Nov 2015 22:24:11 -0800 Subject: [PATCH 0912/1003] Few fixes to make the recorder.js run --- examples/utilities/record/recorder.js | 2 +- libraries/avatars/src/AvatarData.cpp | 6 +++--- libraries/recording/src/recording/Deck.cpp | 2 ++ libraries/recording/src/recording/Frame.h | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/utilities/record/recorder.js b/examples/utilities/record/recorder.js index 13ea1e0c72..b44cb23aa8 100644 --- a/examples/utilities/record/recorder.js +++ b/examples/utilities/record/recorder.js @@ -176,7 +176,7 @@ function formatTime(time) { var SEC_PER_MIN = 60; var MSEC_PER_SEC = 1000; - time = time * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR); + time = time * (MSEC_PER_SEC); var hours = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR)); time -= hours * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e2d2c05ef6..e44219977b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -809,7 +809,7 @@ float AvatarData::playerElapsed() { Q_RETURN_ARG(float, result)); return result; } - return (float)_player->position(); + return (float)_player->position() / (float) MSECS_PER_SECOND; } float AvatarData::playerLength() { @@ -822,7 +822,7 @@ float AvatarData::playerLength() { Q_RETURN_ARG(float, result)); return result; } - return _player->length(); + return (float)_player->length() / (float) MSECS_PER_SECOND; } void AvatarData::loadRecording(const QString& filename) { @@ -870,7 +870,7 @@ void AvatarData::setPlayerTime(float time) { return; } - _player->seek(time); + _player->seek(time * MSECS_PER_SECOND); } void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) { diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 4349a39732..10209c26d7 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -25,6 +25,8 @@ void Deck::queueClip(ClipPointer clip, Time timeOffset) { // FIXME if the time offset is not zero, wrap the clip in a OffsetClip wrapper _clips.push_back(clip); + + _length = std::max(_length, clip->duration()); } void Deck::play() { diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index 85f5246a4e..f0f53ce144 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -27,7 +27,7 @@ public: static const FrameType TYPE_INVALID = 0xFFFF; static const FrameType TYPE_HEADER = 0x0; FrameType type { TYPE_INVALID }; - Time timeOffset { 0 }; + Time timeOffset { 0 }; // milliseconds QByteArray data; Frame() {} From 7979fb38cfa526bcd300f7b37726f5ffdc76a6fd Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 09:25:17 -0800 Subject: [PATCH 0913/1003] don't always send actiondata along with other physics related properties --- libraries/entities/src/EntityTree.cpp | 1 - libraries/physics/src/EntityMotionState.cpp | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7cbdc8b666..0e2b713b65 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -198,7 +198,6 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI properties.setVelocityChanged(false); properties.setAngularVelocityChanged(false); properties.setAccelerationChanged(false); - properties.setActionDataChanged(false); if (wantTerseEditLogging()) { qCDebug(entities) << senderNode->getUUID() << "physical edits suppressed"; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 24957bbf30..759b87f6c6 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -294,7 +294,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { if (_entity->actionDataNeedsTransmit()) { setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY); - _entity->setActionDataNeedsTransmit(false); return true; } @@ -457,7 +456,10 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q properties.setVelocity(_serverVelocity); properties.setAcceleration(_serverAcceleration); properties.setAngularVelocity(_serverAngularVelocity); - properties.setActionData(_serverActionData); + if (_entity->actionDataNeedsTransmit()) { + _entity->setActionDataNeedsTransmit(false); + properties.setActionData(_serverActionData); + } // set the LastEdited of the properties but NOT the entity itself quint64 now = usecTimestampNow(); From 5acbb7436262cd5abefcb07be85c1d3d158fbda7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 09:45:06 -0800 Subject: [PATCH 0914/1003] send to network when an action is deleted, also --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 807186a304..d4eab00075 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1749,6 +1749,7 @@ void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) { assertLocked(); foreach(QUuid actionID, _actionsToRemove) { removeActionInternal(actionID, simulation); + setActionDataNeedsTransmit(true); } _actionsToRemove.clear(); } From 3f7f5a2bbd7c3015b0fe38e4a296d69e0112e56c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 09:49:31 -0800 Subject: [PATCH 0915/1003] send to network when an action is deleted, also --- libraries/entities/src/EntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d4eab00075..d434f0bb7f 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1749,6 +1749,7 @@ void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) { assertLocked(); foreach(QUuid actionID, _actionsToRemove) { removeActionInternal(actionID, simulation); + setActionDataDirty(true); setActionDataNeedsTransmit(true); } _actionsToRemove.clear(); From 6ec5b12081bd52b1ba602e824d5d1918208fc27e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 09:52:35 -0800 Subject: [PATCH 0916/1003] send to network when an action is deleted, also --- libraries/entities/src/EntityItem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index d434f0bb7f..4456f2e751 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1629,6 +1629,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s bool success = true; serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + setActionDataNeedsTransmit(true); return success; } return false; @@ -1749,8 +1750,6 @@ void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) { assertLocked(); foreach(QUuid actionID, _actionsToRemove) { removeActionInternal(actionID, simulation); - setActionDataDirty(true); - setActionDataNeedsTransmit(true); } _actionsToRemove.clear(); } From 1d0a2ddd3d80cdbb17b1427c291461837a37a082 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 11:15:57 -0800 Subject: [PATCH 0917/1003] grab debugging script --- examples/grabInspector.js | 137 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 examples/grabInspector.js diff --git a/examples/grabInspector.js b/examples/grabInspector.js new file mode 100644 index 0000000000..8a027f819a --- /dev/null +++ b/examples/grabInspector.js @@ -0,0 +1,137 @@ +// +// grabInspector.js +// examples +// +// Created by Seth Alves on 2015-9-30. +// 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/utils.js"); + +var INSPECT_RADIUS = 10; +var overlays = {}; + +var toType = function(obj) { + return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() +} + +function grabDataToString(grabData) { + var result = ""; + + for (var argumentName in grabData) { + if (grabData.hasOwnProperty(argumentName)) { + if (argumentName == "type") { + continue; + } + var arg = grabData[argumentName]; + var argType = toType(arg); + var argString = arg; + if (argType == "object") { + if (Object.keys(arg).length == 3) { + argString = vec3toStr(arg, 1); + } + } else if (argType == "number") { + argString = arg.toFixed(2); + } + result += argumentName + ": " + // + toType(arg) + " -- " + + argString + "\n"; + } + } + + return result; +} + + + +function updateOverlay(entityID, grabText) { + var properties = Entities.getEntityProperties(entityID, ["position", "dimensions"]); + var position = Vec3.sum(properties.position, {x:0, y:properties.dimensions.y, z:0}); + if (entityID in overlays) { + var overlay = overlays[entityID]; + Overlays.editOverlay(overlay, { + text: grabText, + position: position + }); + } else { + var lines = grabText.split(/\r\n|\r|\n/); + + var maxLineLength = lines[0].length; + for (var i = 1; i < lines.length; i++) { + if (lines[i].length > maxLineLength) { + maxLineLength = lines[i].length; + } + } + + var textWidth = maxLineLength * 0.034; // XXX how to know this? + var textHeight = .5; + var numberOfLines = lines.length; + var textMargin = 0.05; + var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines; + + overlays[entityID] = Overlays.addOverlay("text3d", { + position: position, + dimensions: { x: textWidth, y: textHeight }, + backgroundColor: { red: 0, green: 0, blue: 0}, + color: { red: 255, green: 255, blue: 255}, + topMargin: textMargin, + leftMargin: textMargin, + bottomMargin: textMargin, + rightMargin: textMargin, + text: grabText, + lineHeight: lineHeight, + alpha: 0.9, + backgroundAlpha: 0.9, + ignoreRayIntersection: true, + visible: true, + isFacingAvatar: true + }); + } +} + + +function cleanup() { + for (var entityID in overlays) { + Overlays.deleteOverlay(overlays[entityID]); + } +} + + +Script.setInterval(function() { + var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS); + for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) { + var entityID = nearbyEntities[entityIndex]; + var userData = getEntityUserData(entityID); + var grabData = userData["grabKey"] + + // {"grabbableKey":{"invertSolidWhileHeld":true}, + // "grabKey":{"activated":true,"avatarId":"{6ea8b092-10e0-4058-888b-6facc40d0fe9}","refCount":1,"gravity":{"x":0,"y":0,"z":0},"ignoreForCollisions":0,"collisionsWillMove":1} + // } + + if (typeof grabData != 'undefined') { + var grabText = grabDataToString(grabData); + updateOverlay(entityID, grabText); + } else { + if (entityID in overlays) { + Overlays.deleteOverlay(overlays[entityID]); + delete overlays[entityID]; + } + } + } + + // if an entity is too far away, remove its overlay + for (var entityID in overlays) { + var position = Entities.getEntityProperties(entityID, ["position"]).position; + if (Vec3.distance(position, MyAvatar.position) > INSPECT_RADIUS) { + Overlays.deleteOverlay(overlays[entityID]); + delete overlays[entityID]; + } + } + +}, 100); + + +Script.scriptEnding.connect(cleanup); From 919ea5f1a05f42645db8678f79af83cd9e264058 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 12:37:07 -0800 Subject: [PATCH 0918/1003] send action updates even if we aren't the simulation owner --- libraries/physics/src/EntityMotionState.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 759b87f6c6..181ae7060e 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -370,6 +370,10 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s assert(_body); assert(entityTreeIsLocked()); + if (_entity->actionDataNeedsTransmit()) { + return true; + } + if (_entity->getSimulatorID() != sessionID) { // we don't own the simulation, but maybe we should... if (_outgoingPriority != NO_PRORITY) { From 37469206552b12e270cdabf5a8323f227e33edae Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 13:56:30 -0800 Subject: [PATCH 0919/1003] allow action updates from others when we are the simulation owner --- examples/grab.js | 4 +++- libraries/entities/src/EntityItem.cpp | 12 +----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index ee6c3c4de5..1637e1bcf2 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -506,6 +506,7 @@ Grabber.prototype.activateEntity = function(entityID, grabbedProperties) { if (data["refCount"] == 1) { data["gravity"] = grabbedProperties.gravity; data["ignoreForCollisions"] = grabbedProperties.ignoreForCollisions; + data["collisionsWillMove"] = grabbedProperties.collisionsWillMove; var whileHeldProperties = {gravity: {x:0, y:0, z:0}}; if (invertSolidWhileHeld) { whileHeldProperties["ignoreForCollisions"] = ! grabbedProperties.ignoreForCollisions; @@ -522,7 +523,8 @@ Grabber.prototype.deactivateEntity = function(entityID) { if (data["refCount"] < 1) { Entities.editEntity(entityID, { gravity: data["gravity"], - ignoreForCollisions: data["ignoreForCollisions"] + ignoreForCollisions: data["ignoreForCollisions"], + collisionsWillMove: data["collisionsWillMove"] }); data = null; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4456f2e751..f032dcd347 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -707,17 +707,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL); READ_ENTITY_PROPERTY(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); - - { // When we own the simulation we don't accept updates to the entity's actions - // but since we're using macros below we have to temporarily modify overwriteLocalData. - // NOTE: this prevents userB from adding an action to an object1 when UserA - // has simulation ownership of it. - // TODO: figure out how to allow multiple users to update actions simultaneously - bool oldOverwrite = overwriteLocalData; - overwriteLocalData = overwriteLocalData && !weOwnSimulation; - READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData); - overwriteLocalData = oldOverwrite; - } + READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); From 91bc7ca0627e62fa8e22373a2ebcb2edb94e55c6 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Fri, 13 Nov 2015 14:04:36 -0800 Subject: [PATCH 0920/1003] cleanup start/stop logic for HMD follow --- interface/src/avatar/MyAvatar.cpp | 111 ++++++++---------- interface/src/avatar/MyAvatar.h | 12 +- .../src/avatar/MyCharacterController.cpp | 20 ++-- interface/src/avatar/MyCharacterController.h | 8 +- 4 files changed, 64 insertions(+), 87 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5e14c66ff1..9666e21ce0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -177,9 +177,8 @@ void MyAvatar::reset(bool andReload) { // Reset dynamic state. _wasPushing = _isPushing = _isBraking = _billboardValid = false; - _isFollowingHMD = false; - _hmdFollowVelocity = Vectors::ZERO; - _hmdFollowSpeed = 0.0f; + _followVelocity = Vectors::ZERO; + _followSpeed = 0.0f; _skeletonModel.reset(); getHead()->reset(); _targetVelocity = glm::vec3(0.0f); @@ -352,52 +351,39 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { void MyAvatar::updateHMDFollowVelocity() { // compute offset to body's target position (in sensor-frame) auto sensorBodyMatrix = deriveBodyFromHMDSensor(); - _hmdFollowOffset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); - glm::vec3 truncatedOffset = _hmdFollowOffset; - if (truncatedOffset.y < 0.0f) { - // don't pull the body DOWN to match the target (allow animation system to squat) - truncatedOffset.y = 0.0f; - } - float truncatedOffsetDistance = glm::length(truncatedOffset); + glm::vec3 offset = extractTranslation(sensorBodyMatrix) - extractTranslation(_bodySensorMatrix); + _followOffsetDistance = glm::length(offset); + + const float FOLLOW_TIMESCALE = 0.5f; + const float FOLLOW_THRESHOLD_SPEED = 0.2f; + const float FOLLOW_MIN_DISTANCE = 0.02f; + const float FOLLOW_THRESHOLD_DISTANCE = 0.2f; - bool isMoving; - if (_lastIsMoving) { - const float MOVE_EXIT_SPEED_THRESHOLD = 0.07f; // m/sec - isMoving = glm::length(_velocity) >= MOVE_EXIT_SPEED_THRESHOLD; - } else { - const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec - isMoving = glm::length(_velocity) > MOVE_ENTER_SPEED_THRESHOLD; - } - bool justStartedMoving = (_lastIsMoving != isMoving) && isMoving; - _lastIsMoving = isMoving; bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); - const float MIN_HMD_HIP_SHIFT = 0.05f; - if (justStartedMoving || (hmdIsAtRest && truncatedOffsetDistance > MIN_HMD_HIP_SHIFT)) { - _isFollowingHMD = true; - } + bool avatarIsMoving = glm::length(_velocity - _followVelocity) > FOLLOW_THRESHOLD_SPEED; + bool shouldFollow = hmdIsAtRest || avatarIsMoving; - bool needNewFollowSpeed = (_isFollowingHMD && _hmdFollowSpeed == 0.0f); - if (!needNewFollowSpeed) { - // check to see if offset has exceeded its threshold - const float MAX_HMD_HIP_SHIFT = 0.2f; - if (truncatedOffsetDistance > MAX_HMD_HIP_SHIFT) { - _isFollowingHMD = true; - needNewFollowSpeed = true; + // linear part + _followOffsetDistance = glm::length(offset); + if (_followOffsetDistance < FOLLOW_MIN_DISTANCE) { + // close enough + _followOffsetDistance = 0.0f; + } else { + glm::vec3 truncatedOffset = offset; + if (truncatedOffset.y < 0.0f) { + truncatedOffset.y = 0.0f; + } + float truncatedDistance = glm::length(truncatedOffset); + bool needsNewSpeed = truncatedDistance > FOLLOW_THRESHOLD_DISTANCE; + if (needsNewSpeed || (shouldFollow && _followSpeed == 0.0f)) { + // compute new speed + _followSpeed = _followOffsetDistance / FOLLOW_TIMESCALE; + } + if (_followSpeed > 0.0f) { + // to compute new velocity we must rotate offset into the world-frame + glm::quat sensorToWorldRotation = extractRotation(_sensorToWorldMatrix); + _followVelocity = _followSpeed * glm::normalize(sensorToWorldRotation * offset); } - } - if (_isFollowingHMD) { - // only bother to rotate into world frame if we're following - glm::quat sensorToWorldRotation = extractRotation(_sensorToWorldMatrix); - _hmdFollowOffset = sensorToWorldRotation * _hmdFollowOffset; - } - if (needNewFollowSpeed) { - // compute new velocity that will be used to resolve offset of hips from body - const float FOLLOW_HMD_DURATION = 0.5f; // seconds - _hmdFollowVelocity = (_hmdFollowOffset / FOLLOW_HMD_DURATION); - _hmdFollowSpeed = glm::length(_hmdFollowVelocity); - } else if (_isFollowingHMD) { - // compute new velocity (but not new speed) - _hmdFollowVelocity = _hmdFollowSpeed * glm::normalize(_hmdFollowOffset); } } @@ -1300,11 +1286,11 @@ void MyAvatar::prepareForPhysicsSimulation() { _characterController.setAvatarPositionAndOrientation(getPosition(), getOrientation()); if (qApp->isHMDMode()) { updateHMDFollowVelocity(); - } else if (_isFollowingHMD) { - _isFollowingHMD = false; - _hmdFollowVelocity = Vectors::ZERO; + } else if (_followSpeed > 0.0f) { + _followVelocity = Vectors::ZERO; + _followSpeed = 0.0f; } - _characterController.setHMDVelocity(_hmdFollowVelocity); + _characterController.setFollowVelocity(_followVelocity); } void MyAvatar::harvestResultsFromPhysicsSimulation() { @@ -1312,35 +1298,27 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() { glm::quat orientation = getOrientation(); _characterController.getAvatarPositionAndOrientation(position, orientation); nextAttitude(position, orientation); - if (_isFollowingHMD) { - setVelocity(_characterController.getLinearVelocity() + _hmdFollowVelocity); - glm::vec3 hmdShift = _characterController.getHMDShift(); - adjustSensorTransform(hmdShift); + if (_followSpeed > 0.0f) { + adjustSensorTransform(); + setVelocity(_characterController.getLinearVelocity() + _followVelocity); } else { setVelocity(_characterController.getLinearVelocity()); } } -void MyAvatar::adjustSensorTransform(glm::vec3 hmdShift) { +void MyAvatar::adjustSensorTransform() { // compute blendFactor of latest hmdShift // which we'll use to blend the rotation part - float blendFactor = 1.0f; - float shiftLength = glm::length(hmdShift); - if (shiftLength > 1.0e-5f) { - float offsetLength = glm::length(_hmdFollowOffset); - if (offsetLength > shiftLength) { - blendFactor = shiftLength / offsetLength; - } - } + float linearDistance = _characterController.getFollowTime() * _followSpeed; + float blendFactor = linearDistance < _followOffsetDistance ? linearDistance / _followOffsetDistance : 1.0f; auto newBodySensorMatrix = deriveBodyFromHMDSensor(); auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; glm::quat finalBodyRotation = glm::normalize(glm::quat_cast(worldBodyMatrix)); if (blendFactor >= 0.99f) { // the "adjustment" is more or less complete so stop following - _isFollowingHMD = false; - _hmdFollowSpeed = 0.0f; - _hmdFollowVelocity = Vectors::ZERO; + _followVelocity = Vectors::ZERO; + _followSpeed = 0.0f; // and slam the body's transform anyway to eliminate any slight errors glm::vec3 finalBodyPosition = extractTranslation(worldBodyMatrix); nextAttitude(finalBodyPosition, finalBodyRotation); @@ -1520,6 +1498,9 @@ void MyAvatar::initAnimGraph() { QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full/avatar-animation.json") : _animGraphUrl); _rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); + + _bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. + updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes } void MyAvatar::destroyAnimGraph() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 52f1ffce3f..e1c733c625 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -208,7 +208,7 @@ public: void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(); - void adjustSensorTransform(glm::vec3 hmdShift); + void adjustSensorTransform(); const QString& getCollisionSoundURL() { return _collisionSoundURL; } void setCollisionSoundURL(const QString& url); @@ -394,9 +394,10 @@ private: // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. glm::mat4 _sensorToWorldMatrix; - glm::vec3 _hmdFollowOffset { Vectors::ZERO }; - glm::vec3 _hmdFollowVelocity { Vectors::ZERO }; - float _hmdFollowSpeed { 0.0f }; + + glm::vec3 _followVelocity { Vectors::ZERO }; + float _followSpeed { 0.0f }; + float _followOffsetDistance { 0.0f }; bool _goToPending; glm::vec3 _goToPosition; @@ -414,9 +415,6 @@ private: glm::vec3 _customListenPosition; glm::quat _customListenOrientation; - bool _isFollowingHMD { false }; - float _followHMDAlpha { 0.0f }; - AtRestDetector _hmdAtRestDetector; bool _lastIsMoving { false }; }; diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index ad2ca32b05..e8f686da6f 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -60,7 +60,7 @@ MyCharacterController::MyCharacterController(MyAvatar* avatar) { _floorDistance = MAX_FALL_HEIGHT; _walkVelocity.setValue(0.0f, 0.0f, 0.0f); - _hmdVelocity.setValue(0.0f, 0.0f, 0.0f); + _followVelocity.setValue(0.0f, 0.0f, 0.0f); _jumpSpeed = JUMP_SPEED; _isOnGround = false; _isJumping = false; @@ -68,7 +68,7 @@ MyCharacterController::MyCharacterController(MyAvatar* avatar) { _isHovering = true; _isPushingUp = false; _jumpToHoverStart = 0; - _lastStepDuration = 0.0f; + _followTime = 0.0f; _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; updateShapeIfNecessary(); @@ -161,16 +161,14 @@ void MyCharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) } } - // Rather than add _hmdVelocity to the velocity of the RigidBody, we explicitly teleport + // Rather than add _followVelocity to the velocity of the RigidBody, we explicitly teleport // the RigidBody forward according to the formula: distance = rate * time - if (_hmdVelocity.length2() > 0.0f) { + if (_followVelocity.length2() > 0.0f) { btTransform bodyTransform = _rigidBody->getWorldTransform(); - bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _hmdVelocity); + bodyTransform.setOrigin(bodyTransform.getOrigin() + dt * _followVelocity); _rigidBody->setWorldTransform(bodyTransform); } - // MyAvatar will ask us how far we stepped for HMD motion, which will depend on how - // much time has accumulated in _lastStepDuration. - _lastStepDuration += dt; + _followTime += dt; } void MyCharacterController::jump() { @@ -346,8 +344,8 @@ void MyCharacterController::setTargetVelocity(const glm::vec3& velocity) { _walkVelocity = glmToBullet(velocity); } -void MyCharacterController::setHMDVelocity(const glm::vec3& velocity) { - _hmdVelocity = glmToBullet(velocity); +void MyCharacterController::setFollowVelocity(const glm::vec3& velocity) { + _followVelocity = glmToBullet(velocity); } glm::vec3 MyCharacterController::getLinearVelocity() const { @@ -400,7 +398,7 @@ void MyCharacterController::preSimulation() { } } } - _lastStepDuration = 0.0f; + _followTime = 0.0f; } void MyCharacterController::postSimulation() { diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index de711c84f4..82aa958309 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -64,8 +64,8 @@ public: void getAvatarPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; void setTargetVelocity(const glm::vec3& velocity); - void setHMDVelocity(const glm::vec3& velocity); - glm::vec3 getHMDShift() const { return _lastStepDuration * bulletToGLM(_hmdVelocity); } + void setFollowVelocity(const glm::vec3& velocity); + float getFollowTime() const { return _followTime; } glm::vec3 getLinearVelocity() const; @@ -75,7 +75,7 @@ protected: protected: btVector3 _currentUp; btVector3 _walkVelocity; - btVector3 _hmdVelocity; + btVector3 _followVelocity; btTransform _avatarBodyTransform; glm::vec3 _shapeLocalOffset; @@ -93,7 +93,7 @@ protected: btScalar _gravity; btScalar _jumpSpeed; - btScalar _lastStepDuration; + btScalar _followTime; bool _enabled; bool _isOnGround; From 3b6b56f316574fd8d41c02e05e6b92c6d133705e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 14:26:43 -0800 Subject: [PATCH 0921/1003] formatting --- libraries/render/src/render/DrawStatus.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index 83be3112e0..1239819911 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -21,8 +21,8 @@ namespace render { int _drawItemBoundDimLoc = -1; int _drawItemStatusPosLoc = -1; int _drawItemStatusDimLoc = -1; - int _drawItemStatusValue0Loc = -1; - int _drawItemStatusValue1Loc = -1; + int _drawItemStatusValue0Loc = -1; + int _drawItemStatusValue1Loc = -1; gpu::Stream::FormatPointer _drawItemFormat; gpu::PipelinePointer _drawItemBoundsPipeline; From ac635336b79ae5e77df76b1c86ab3e3d2970fa89 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 14:58:17 -0800 Subject: [PATCH 0922/1003] split AvatarActionHold's finding of its location target into a new function --- interface/src/avatar/AvatarActionHold.cpp | 48 ++++++++++++----------- interface/src/avatar/AvatarActionHold.h | 5 +++ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 839f2d4fbb..ca7fcfb187 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -10,7 +10,6 @@ // #include "QVariantGLM.h" -#include "avatar/MyAvatar.h" #include "avatar/AvatarManager.h" #include "AvatarActionHold.h" @@ -22,8 +21,7 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit _relativePosition(glm::vec3(0.0f)), _relativeRotation(glm::quat()), _hand("right"), - _holderID(QUuid()) -{ + _holderID(QUuid()) { _type = ACTION_TYPE_HOLD; #if WANT_DEBUG qDebug() << "AvatarActionHold::AvatarActionHold"; @@ -36,13 +34,10 @@ AvatarActionHold::~AvatarActionHold() { #endif } -void AvatarActionHold::updateActionWorker(float deltaTimeStep) { - bool gotLock = false; - glm::quat rotation; - glm::vec3 position; +std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) { std::shared_ptr holdingAvatar = nullptr; - gotLock = withTryReadLock([&]{ + withTryReadLock([&]{ QSharedPointer avatarManager = DependencyManager::get(); AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); holdingAvatar = std::static_pointer_cast(holdingAvatarData); @@ -65,22 +60,28 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { } }); + return holdingAvatar; +} + +void AvatarActionHold::updateActionWorker(float deltaTimeStep) { + glm::quat rotation; + glm::vec3 position; + std::shared_ptr holdingAvatar = getTarget(rotation, position); + if (holdingAvatar) { + bool gotLock = withTryWriteLock([&]{ + _positionalTarget = position; + _rotationalTarget = rotation; + _positionalTargetSet = true; + _rotationalTargetSet = true; + _active = true; + }); if (gotLock) { - gotLock = withTryWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - _positionalTargetSet = true; - _rotationalTargetSet = true; - _active = true; - }); - if (gotLock) { - if (_kinematic) { - doKinematicUpdate(deltaTimeStep); - } else { - activateBody(); - ObjectActionSpring::updateActionWorker(deltaTimeStep); - } + if (_kinematic) { + doKinematicUpdate(deltaTimeStep); + } else { + activateBody(); + ObjectActionSpring::updateActionWorker(deltaTimeStep); } } } @@ -109,7 +110,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { if (_previousSet) { // smooth velocity over 2 frames glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget; - glm::vec3 positionalVelocity = (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); + glm::vec3 positionalVelocity = + (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep); rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); _previousPositionalDelta = positionalDelta; _previousDeltaTimeStep = deltaTimeStep; diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 15a096d1ce..b8b1a64e84 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -17,6 +17,9 @@ #include #include +#include "avatar/MyAvatar.h" + + class AvatarActionHold : public ObjectActionSpring { public: AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity); @@ -32,6 +35,8 @@ public: virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); } + std::shared_ptr getTarget(glm::quat& rotation, glm::vec3& position); + private: static const uint16_t holdVersion; From eb0d91fc7eca8405b17006bb7ff760a88ac5a60b Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Thu, 12 Nov 2015 18:25:51 -0800 Subject: [PATCH 0923/1003] Fixes to Master recording for ACs --- examples/acScripts/ControlACs.js | 57 +++++++++++++++--------------- examples/acScripts/ControlledAC.js | 54 ++++++++++++---------------- 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index 60b72446bb..c0d5043e26 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -33,15 +33,6 @@ var SHOW = 4; var HIDE = 5; var LOAD = 6; -var COLORS = []; -COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 }; -COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 }; -COLORS[STOP] = { red: STOP, green: 0, blue: 0 }; -COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 }; -COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 }; -COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 }; - - var windowDimensions = Controller.getViewportDimensions(); var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/"; @@ -138,6 +129,7 @@ function setupToolBars() { } function sendCommand(id, action) { + if (action === SHOW) { toolBars[id].selectTool(onOffIcon[id], false); toolBars[id].setAlpha(ALPHA_ON, playIcon[id]); @@ -154,24 +146,29 @@ function sendCommand(id, action) { return; } - if (id === (toolBars.length - 1)) { - for (i = 0; i < NUM_AC; i++) { - sendCommand(i, action); - } - return; - } + if (id === (toolBars.length - 1)) + id = -1; - var position = { x: controlEntityPosition.x + id * controlEntitySize, - y: controlEntityPosition.y, z: controlEntityPosition.z }; - Entities.addEntity({ - name: "Actor Controller", - userData: clip_url, + var controlEntity = Entities.addEntity({ + name: 'New Actor Controller', type: "Box", - position: position, - dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, - color: COLORS[action], - lifetime: 5 - }); + color: { red: 0, green: 0, blue: 0 }, + position: controlEntityPosition, + dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, + visible: false, + lifetime: 10, + userData: JSON.stringify({ + idKey: { + uD_id: id + }, + actionKey: { + uD_action: action + }, + clipKey: { + uD_url: clip_url + } + }) + }); } function mousePressEvent(event) { @@ -191,8 +188,12 @@ function mousePressEvent(event) { sendCommand(i, PLAY_LOOP); } else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { sendCommand(i, STOP); - } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { - sendCommand(i, LOAD); + } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { + input_text = Window.prompt("Insert the url of the clip: ",""); + if(!(input_text === "" || input_text === null)) { + clip_url = input_text; + sendCommand(i, LOAD); + } } else { // Check individual controls for (i = 0; i < NUM_AC; i++) { @@ -210,7 +211,7 @@ function mousePressEvent(event) { sendCommand(i, STOP); } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { input_text = Window.prompt("Insert the url of the clip: ",""); - if(!(input_text === "" || input_text === null)){ + if(!(input_text === "" || input_text === null)) { clip_url = input_text; sendCommand(i, LOAD); } diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index 4eecc11136..de31f66df7 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -38,18 +38,6 @@ var SHOW = 4; var HIDE = 5; var LOAD = 6; -var COLORS = []; -COLORS[PLAY] = { red: PLAY, green: 0, blue: 0 }; -COLORS[PLAY_LOOP] = { red: PLAY_LOOP, green: 0, blue: 0 }; -COLORS[STOP] = { red: STOP, green: 0, blue: 0 }; -COLORS[SHOW] = { red: SHOW, green: 0, blue: 0 }; -COLORS[HIDE] = { red: HIDE, green: 0, blue: 0 }; -COLORS[LOAD] = { red: LOAD, green: 0, blue: 0 }; - -controlEntityPosition.x += id * controlEntitySize; - -Avatar.loadRecording(clip_url); - Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); Avatar.setPlayerUseDisplayName(useDisplayName); Avatar.setPlayerUseAttachments(useAttachments); @@ -67,27 +55,29 @@ function setupEntityViewer() { EntityViewer.queryOctree(); } -function getAction(controlEntity) { - clip_url = controlEntity.userData; +function getAction(controlEntity) { + if (controlEntity === null) { + return DO_NOTHING; + } + + var userData = JSON.parse(Entities.getEntityProperties(controlEntity, ["userData"]).userData); - if (controlEntity === null || - controlEntity.position.x !== controlEntityPosition.x || - controlEntity.position.y !== controlEntityPosition.y || - controlEntity.position.z !== controlEntityPosition.z || - controlEntity.dimensions.x !== controlEntitySize) { + var uD_id = userData.idKey.uD_id; + var uD_action = userData.actionKey.uD_action; + var uD_url = userData.clipKey.uD_url; + + print("Sono " + id + " e ho ricevuto un comando da " + uD_id); + + Entities.deleteEntity((Entities.getEntityProperties(controlEntity)).id); + + if(uD_id === id || uD_id === -1) { + if(uD_action === 6) + clip_url = uD_url; + + return uD_action; + } else { return DO_NOTHING; - } - - for (i in COLORS) { - if (controlEntity.color.red === COLORS[i].red && - controlEntity.color.green === COLORS[i].green && - controlEntity.color.blue === COLORS[i].blue) { - Entities.deleteEntity(controlEntity.id); - return parseInt(i); - } - } - - return DO_NOTHING; + } } count = 100; // This is necessary to wait for the audio mixer to connect @@ -100,7 +90,7 @@ function update(event) { var controlEntity = Entities.findClosestEntity(controlEntityPosition, controlEntitySize); - var action = getAction(Entities.getEntityProperties(controlEntity)); + var action = getAction(controlEntity); switch(action) { case PLAY: From a80871a7a85b2c70f3152db238fd06a74eea78c1 Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Fri, 13 Nov 2015 14:58:59 -0800 Subject: [PATCH 0924/1003] Fixes --- examples/acScripts/ControlACs.js | 4 ++-- examples/acScripts/ControlledAC.js | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index c0d5043e26..ba066d9750 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -190,7 +190,7 @@ function mousePressEvent(event) { sendCommand(i, STOP); } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { input_text = Window.prompt("Insert the url of the clip: ",""); - if(!(input_text === "" || input_text === null)) { + if (!(input_text === "" || input_text === null)) { clip_url = input_text; sendCommand(i, LOAD); } @@ -211,7 +211,7 @@ function mousePressEvent(event) { sendCommand(i, STOP); } else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) { input_text = Window.prompt("Insert the url of the clip: ",""); - if(!(input_text === "" || input_text === null)) { + if (!(input_text === "" || input_text === null)) { clip_url = input_text; sendCommand(i, LOAD); } diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index de31f66df7..41a8a2b257 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -66,12 +66,10 @@ function getAction(controlEntity) { var uD_action = userData.actionKey.uD_action; var uD_url = userData.clipKey.uD_url; - print("Sono " + id + " e ho ricevuto un comando da " + uD_id); - Entities.deleteEntity((Entities.getEntityProperties(controlEntity)).id); - if(uD_id === id || uD_id === -1) { - if(uD_action === 6) + if (uD_id === id || uD_id === -1) { + if (uD_action === 6) clip_url = uD_url; return uD_action; From b0d24be58fc854da97ee94625a6dad2dc864b5ed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 Nov 2015 16:02:39 -0800 Subject: [PATCH 0925/1003] add a way to get a list of all actions of a certain type from an entity. hold actions average their positional targets. --- interface/src/avatar/AvatarActionHold.cpp | 29 +++++++++++++++++-- .../entities/src/EntityActionInterface.h | 10 +++++-- libraries/entities/src/EntityItem.cpp | 15 ++++++++++ libraries/entities/src/EntityItem.h | 5 +++- libraries/entities/src/EntityTypes.h | 4 +-- libraries/physics/src/ObjectAction.h | 1 - 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index ca7fcfb187..8e13fa8385 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -66,9 +66,34 @@ std::shared_ptr AvatarActionHold::getTarget(glm::quat& rotation, glm::ve void AvatarActionHold::updateActionWorker(float deltaTimeStep) { glm::quat rotation; glm::vec3 position; - std::shared_ptr holdingAvatar = getTarget(rotation, position); + bool valid = false; + int holdCount = 0; + + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return; + } + QList holdActions = ownerEntity->getActionsOfType(ACTION_TYPE_HOLD); + foreach (EntityActionPointer action, holdActions) { + std::shared_ptr holdAction = std::static_pointer_cast(action); + glm::quat rotationForAction; + glm::vec3 positionForAction; + std::shared_ptr holdingAvatar = holdAction->getTarget(rotationForAction, positionForAction); + if (holdingAvatar) { + holdCount ++; + if (holdAction.get() == this) { + // only use the rotation for this action + valid = true; + rotation = rotationForAction; + } + + position += positionForAction; + } + } + + if (valid && holdCount > 0) { + position /= holdCount; - if (holdingAvatar) { bool gotLock = withTryWriteLock([&]{ _positionalTarget = position; _rotationalTarget = rotation; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 01292e3840..a192661e52 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -12,11 +12,14 @@ #ifndef hifi_EntityActionInterface_h #define hifi_EntityActionInterface_h +#include #include +#include -#include "EntityItem.h" - +class EntityItem; class EntitySimulation; +using EntityItemPointer = std::shared_ptr; +using EntityItemWeakPointer = std::weak_ptr; enum EntityActionType { // keep these synchronized with actionTypeFromString and actionTypeToString @@ -34,6 +37,8 @@ public: const QUuid& getID() const { return _id; } EntityActionType getType() const { return _type; } + bool isActive() { return _active; } + virtual void removeFromSimulation(EntitySimulation* simulation) const = 0; virtual EntityItemWeakPointer getOwnerEntity() const = 0; virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0; @@ -81,6 +86,7 @@ protected: QUuid _id; EntityActionType _type; + bool _active { false }; }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f032dcd347..100f6dfe22 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1844,3 +1844,18 @@ bool EntityItem::shouldSuppressLocationEdits() const { return false; } + +QList EntityItem::getActionsOfType(EntityActionType typeToGet) { + QList result; + + QHash::const_iterator i = _objectActions.begin(); + while (i != _objectActions.end()) { + EntityActionPointer action = i.value(); + if (action->getType() == typeToGet && action->isActive()) { + result += action; + } + i++; + } + + return result; +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5b47198e97..5ceccef4b1 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -30,6 +30,7 @@ #include "EntityTypes.h" #include "SimulationOwner.h" #include "SimulationFlags.h" +#include "EntityActionInterface.h" class EntitySimulation; class EntityTreeElement; @@ -419,7 +420,9 @@ public: void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; } const QUuid& getSourceUUID() const { return _sourceUUID; } - bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } + bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } + + QList getActionsOfType(EntityActionType typeToGet); protected: diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 30b6edbc07..3536327d18 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -20,8 +20,8 @@ #include // for RenderArgs class EntityItem; -typedef std::shared_ptr EntityItemPointer; -typedef std::weak_ptr EntityItemWeakPointer; +using EntityItemPointer = std::shared_ptr; +using EntityItemWeakPointer = std::weak_ptr; inline uint qHash(const EntityItemPointer& a, uint seed) { return qHash(a.get(), seed); diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 45b40a9fb3..afb6745e9c 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -67,7 +67,6 @@ protected: EntityItemWeakPointer _ownerEntity; QString _tag; quint64 _expires { 0 }; // in seconds since epoch - bool _active { false }; private: int getEntityServerClockSkew() const; From c8349dda6eaa64dfac5f4bb27ca5aaf118aa4ba4 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Thu, 12 Nov 2015 18:54:10 -0800 Subject: [PATCH 0926/1003] added examples for messages synchronization --- .../entityScripts/synchronizerEntityScript.js | 87 +++++++++++++ examples/entityScripts/synchronizerMaster.js | 119 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 examples/entityScripts/synchronizerEntityScript.js create mode 100644 examples/entityScripts/synchronizerMaster.js diff --git a/examples/entityScripts/synchronizerEntityScript.js b/examples/entityScripts/synchronizerEntityScript.js new file mode 100644 index 0000000000..733cb19eb3 --- /dev/null +++ b/examples/entityScripts/synchronizerEntityScript.js @@ -0,0 +1,87 @@ +// +// synchronizerEntityScript.js +// examples/entityScripts +// +// Created by Alessandro Signa on 11/12/15. +// Copyright 2015 High Fidelity, Inc. +// + +// This script shows how to create a synchronized event between avatars trhough an entity. +// It works using the entity's userData: the master change its value and every client checks it every frame +// This entity prints a message when the event starts and when it ends. +// The client running synchronizerMaster.js is the event master and it decides when the event starts/ends by pressing a button. +// All the avatars in the area when the master presses the button will receive a message. +// + +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + + +(function() { + var insideArea = false; + var isJoiningTheEvent = false; + var _this; + + function update(){ + var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); + var valueToCheck = userData.myKey.valueToCheck; + if(valueToCheck && !isJoiningTheEvent){ + _this.sendMessage(); + }else if((!valueToCheck && isJoiningTheEvent) || (isJoiningTheEvent && !insideArea)){ + _this.stopMessage(); + } + + } + + function ParamsEntity() { + _this = this; + return; + } + + + ParamsEntity.prototype = { + preload: function(entityID) { + print('entity loaded') + this.entityID = entityID; + Script.update.connect(update); + }, + enterEntity: function(entityID) { + print("enterEntity("+entityID+")"); + var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); + var valueToCheck = userData.myKey.valueToCheck; + if(!valueToCheck){ + //i'm in the area in time (before the event starts) + insideArea = true; + } + change(entityID); + }, + leaveEntity: function(entityID) { + print("leaveEntity("+entityID+")"); + Entities.editEntity(entityID, { color: { red: 255, green: 190, blue: 20} }); + insideArea = false; + }, + + sendMessage: function(myID){ + if(insideArea && !isJoiningTheEvent){ + print("The event started"); + isJoiningTheEvent = true; + } + }, + + stopMessage: function(myID){ + if(isJoiningTheEvent){ + print("The event ended"); + isJoiningTheEvent = false; + } + } + } + + function change(entityID) { + Entities.editEntity(entityID, { color: { red: 255, green: 100, blue: 220} }); + } + + + return new ParamsEntity(); +}); diff --git a/examples/entityScripts/synchronizerMaster.js b/examples/entityScripts/synchronizerMaster.js new file mode 100644 index 0000000000..fd4001ff3d --- /dev/null +++ b/examples/entityScripts/synchronizerMaster.js @@ -0,0 +1,119 @@ +// +// synchronizerMaster.js +// examples/entityScripts +// +// Created by Alessandro Signa on 11/12/15. +// Copyright 2015 High Fidelity, Inc. +// +// Run this script to spawn a box (synchronizer) and drive the start/end of the event for anyone who is inside the box +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var PARAMS_SCRIPT_URL = 'https://raw.githubusercontent.com/AlessandroSigna/hifi/27fbef3e873d11648faf0a592bb2314a90c71624/examples/entityScripts/synchronizerEntityScript.js'; + + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +Script.include("../libraries/toolBars.js"); +Script.include("../libraries/utils.js"); + + + +var rotation = Quat.safeEulerAngles(Camera.getOrientation()); +rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(rotation))); + +var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/"; +var ALPHA_ON = 1.0; +var ALPHA_OFF = 0.7; +var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; + +var toolBar = null; +var recordIcon; + + + +var isHappening = false; + +var testEntity = Entities.addEntity({ + name: 'paramsTestEntity', + dimensions: { + x: 2, + y: 1, + z: 2 + }, + type: 'Box', + position: center, + color: { + red: 255, + green: 255, + blue: 255 + }, + visible: true, + ignoreForCollisions: true, + script: PARAMS_SCRIPT_URL, + + userData: JSON.stringify({ + myKey: { + valueToCheck: false + } + }) +}); + + +setupToolBar(); + +function setupToolBar() { + if (toolBar != null) { + print("Multiple calls to setupToolBar()"); + return; + } + Tool.IMAGE_HEIGHT /= 2; + Tool.IMAGE_WIDTH /= 2; + + toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); //put the button in the up-left corner + + toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); + + recordIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-record.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + x: 0, y: 0, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON, + visible: true + }, true, isHappening); + +} + +function mousePressEvent(event) { + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (recordIcon === toolBar.clicked(clickedOverlay, false)) { + if (!isHappening) { + print("I'm the event master. I want the event starts"); + isHappening = true; + setEntityCustomData("myKey", testEntity, {valueToCheck: true}); + + } else { + print("I want the event stops"); + isHappening = false; + setEntityCustomData("myKey", testEntity, {valueToCheck: false}); + + } + } +} + +Script.setTimeout(function() { + print('sending data to entity'); + Entities.callEntityMethod(testEntity, 'testParams', data); +}, 1500) + +function cleanup() { + Entities.deleteEntity(testEntity); +} + + + + Script.scriptEnding.connect(cleanup); + Controller.mousePressEvent.connect(mousePressEvent); \ No newline at end of file From 5b66416b8c1d1e88272e8d63a8bf1ded9a25be59 Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 13 Nov 2015 15:03:36 -0800 Subject: [PATCH 0927/1003] added cleanup before delete --- .../entityScripts/synchronizerEntityScript.js | 25 +++++++++++-------- examples/entityScripts/synchronizerMaster.js | 8 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/entityScripts/synchronizerEntityScript.js b/examples/entityScripts/synchronizerEntityScript.js index 733cb19eb3..82dd954381 100644 --- a/examples/entityScripts/synchronizerEntityScript.js +++ b/examples/entityScripts/synchronizerEntityScript.js @@ -24,16 +24,7 @@ var isJoiningTheEvent = false; var _this; - function update(){ - var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); - var valueToCheck = userData.myKey.valueToCheck; - if(valueToCheck && !isJoiningTheEvent){ - _this.sendMessage(); - }else if((!valueToCheck && isJoiningTheEvent) || (isJoiningTheEvent && !insideArea)){ - _this.stopMessage(); - } - - } + function ParamsEntity() { _this = this; @@ -42,10 +33,19 @@ ParamsEntity.prototype = { + update: function(){ + var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); + var valueToCheck = userData.myKey.valueToCheck; + if(valueToCheck && !isJoiningTheEvent){ + _this.sendMessage(); + }else if((!valueToCheck && isJoiningTheEvent) || (isJoiningTheEvent && !insideArea)){ + _this.stopMessage(); + } + }, preload: function(entityID) { print('entity loaded') this.entityID = entityID; - Script.update.connect(update); + Script.update.connect(_this.update); }, enterEntity: function(entityID) { print("enterEntity("+entityID+")"); @@ -75,6 +75,9 @@ print("The event ended"); isJoiningTheEvent = false; } + }, + clean: function(entityID) { + Script.update.disconnect(_this.update); } } diff --git a/examples/entityScripts/synchronizerMaster.js b/examples/entityScripts/synchronizerMaster.js index fd4001ff3d..e5af46c461 100644 --- a/examples/entityScripts/synchronizerMaster.js +++ b/examples/entityScripts/synchronizerMaster.js @@ -10,7 +10,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -var PARAMS_SCRIPT_URL = 'https://raw.githubusercontent.com/AlessandroSigna/hifi/27fbef3e873d11648faf0a592bb2314a90c71624/examples/entityScripts/synchronizerEntityScript.js'; +var PARAMS_SCRIPT_URL = 'https://raw.githubusercontent.com/AlessandroSigna/hifi/05aa1d4ce49c719353007c245ae77ef2d2a8fc36/examples/entityScripts/synchronizerEntityScript.js'; HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; @@ -104,12 +104,10 @@ function mousePressEvent(event) { } } -Script.setTimeout(function() { - print('sending data to entity'); - Entities.callEntityMethod(testEntity, 'testParams', data); -}, 1500) function cleanup() { + toolBar.cleanup(); + Entities.callEntityMethod(testEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings Entities.deleteEntity(testEntity); } From 5e395713a677a791133255c939c02e7eeeda6c8f Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Fri, 13 Nov 2015 16:33:54 -0800 Subject: [PATCH 0928/1003] fix script url --- examples/entityScripts/synchronizerMaster.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/entityScripts/synchronizerMaster.js b/examples/entityScripts/synchronizerMaster.js index e5af46c461..8b6c8c2b8b 100644 --- a/examples/entityScripts/synchronizerMaster.js +++ b/examples/entityScripts/synchronizerMaster.js @@ -10,7 +10,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -var PARAMS_SCRIPT_URL = 'https://raw.githubusercontent.com/AlessandroSigna/hifi/05aa1d4ce49c719353007c245ae77ef2d2a8fc36/examples/entityScripts/synchronizerEntityScript.js'; +var PARAMS_SCRIPT_URL = Script.resolvePath('synchronizerEntityScript.js'); HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; From 6733767d8b41f16f553ab546aa782f4b2e695674 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Fri, 13 Nov 2015 16:39:40 -0800 Subject: [PATCH 0929/1003] use animation state to compute bodyInSensorFrame --- interface/src/avatar/MyAvatar.cpp | 58 +++++++------------------------ interface/src/avatar/MyAvatar.h | 2 +- libraries/animation/src/Rig.cpp | 24 +++++++++++-- libraries/animation/src/Rig.h | 5 +++ 4 files changed, 40 insertions(+), 49 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9666e21ce0..fae66f5898 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1975,53 +1975,19 @@ glm::quat MyAvatar::getWorldBodyOrientation() const { // derive avatar body position and orientation from the current HMD Sensor location. // results are in sensor space glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { - - // HMD is in sensor space. - const glm::vec3 hmdPosition = getHMDSensorPosition(); - const glm::quat hmdOrientation = getHMDSensorOrientation(); - const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); - - const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); - const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); - const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); - const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f); - - vec3 localEyes, localNeck; - if (!_debugDrawSkeleton) { - const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); - localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); - localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS); - } else { - // TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses. - // for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig. - - // TODO: cache these indices. - int rightEyeIndex = _debugDrawSkeleton->nameToJointIndex("RightEye"); - int leftEyeIndex = _debugDrawSkeleton->nameToJointIndex("LeftEye"); - int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); - int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); - - glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; - glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; - glm::vec3 absHipsPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS; - - const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); - localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); - localNeck = rotY180 * (absNeckPos - absHipsPos); + if (_rig) { + // orientation + const glm::quat hmdOrientation = getHMDSensorOrientation(); + const glm::quat yaw = cancelOutRollAndPitch(hmdOrientation); + // position + // we flip about yAxis when going from "root" to "avatar" frame + // and we must also apply "yaw" to get into HMD frame + glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 eyesInAvatarFrame = rotY180 * yaw * _rig->getEyesInRootFrame(); + glm::vec3 bodyPos = getHMDSensorPosition() - eyesInAvatarFrame; + return createMatFromQuatAndPos(yaw, bodyPos); } - - // apply simplistic head/neck model - // figure out where the avatar body should be by applying offsets from the avatar's neck & head joints. - - // eyeToNeck offset is relative full HMD orientation. - // while neckToRoot offset is only relative to HMDs yaw. - glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes); - glm::vec3 neckToRoot = hmdOrientationYawOnly * -localNeck; - glm::vec3 bodyPos = hmdPosition + eyeToNeck + neckToRoot; - - // avatar facing is determined solely by hmd orientation. - return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos); + return glm::mat4(); } glm::vec3 MyAvatar::getPositionForAudio() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e1c733c625..eaad5b6714 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -330,7 +330,7 @@ private: PalmData getActivePalmData(int palmIndex) const; // derive avatar body position and orientation from the current HMD Sensor location. - // results are in sensor space + // results are in HMD frame glm::mat4 deriveBodyFromHMDSensor() const; float _driveKeys[MAX_DRIVE_KEYS]; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7926b268b5..9b6221a370 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -407,6 +407,24 @@ void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, *alphaOut = alpha; } +void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) { + // TODO: use cached eye/hips indices for these calculations + int numPoses = poses.size(); + int rightEyeIndex = _animSkeleton->nameToJointIndex(QString("RightEye")); + int leftEyeIndex = _animSkeleton->nameToJointIndex(QString("LeftEye")); + if (numPoses > rightEyeIndex && numPoses > leftEyeIndex + && rightEyeIndex > 0 && leftEyeIndex > 0) { + int hipsIndex = _animSkeleton->nameToJointIndex(QString("Hips")); + int headIndex = _animSkeleton->nameToJointIndex(QString("Head")); + if (hipsIndex >= 0 && headIndex > 0) { + glm::vec3 rightEye = _animSkeleton->getAbsolutePose(rightEyeIndex, poses).trans; + glm::vec3 leftEye = _animSkeleton->getAbsolutePose(leftEyeIndex, poses).trans; + glm::vec3 hips = _animSkeleton->getAbsolutePose(hipsIndex, poses).trans; + _eyesInRootFrame = 0.5f * (rightEye + leftEye) - hips; + } + } +} + // animation reference speeds. static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s @@ -730,6 +748,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setJointTranslation((int)i, true, poses[i].trans, PRIORITY); } + computeEyesInRootFrame(poses); } else { // First normalize the fades so that they sum to 1.0. @@ -1124,14 +1143,14 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa static AnimPose avatarToBonePose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) { AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips")); - AnimPose rotY180(glm::vec3(1), glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0)); + AnimPose rotY180(glm::vec3(1.0f), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0)); return rootPose * rotY180 * pose; } #ifdef DEBUG_RENDERING static AnimPose boneToAvatarPose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) { AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips")); - AnimPose rotY180(glm::vec3(1), glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0)); + AnimPose rotY180(glm::vec3(1.0f), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0)); return (rootPose * rotY180).inverse() * pose; } #endif @@ -1342,6 +1361,7 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { void Rig::makeAnimSkeleton(const FBXGeometry& fbxGeometry) { if (!_animSkeleton) { _animSkeleton = std::make_shared(fbxGeometry); + computeEyesInRootFrame(_animSkeleton->getRelativeBindPoses()); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 98847b9915..98d3a30392 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -214,6 +214,8 @@ public: bool getModelOffset(glm::vec3& modelOffsetOut) const; + const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } + protected: void updateAnimationStateHandlers(); @@ -222,6 +224,8 @@ public: void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; + void computeEyesInRootFrame(const AnimPoseVec& poses); + QVector _jointStates; int _rootJointIndex = -1; @@ -241,6 +245,7 @@ public: glm::vec3 _lastFront; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; + glm::vec3 _eyesInRootFrame { Vectors::ZERO }; std::shared_ptr _animNode; std::shared_ptr _animSkeleton; From 8c21ac144ee63642461e457bdcafcf0556ddf6db Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 13 Nov 2015 16:45:19 -0800 Subject: [PATCH 0930/1003] Fixing review comments --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/AvatarData.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4d62946a5f..278cc5ff81 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -608,7 +608,7 @@ float MyAvatar::recorderElapsed() { if (!_recorder) { return 0; } - return (float)_recorder->position(); + return (float)_recorder->position() / (float) MSECS_PER_SECOND; } QMetaObject::Connection _audioClientRecorderConnection; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e44219977b..8537f37f84 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1664,7 +1664,7 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { auto worldTransform = currentBasis->worldTransform(relativeTransform); _avatar->setPosition(worldTransform.getTranslation()); _avatar->setOrientation(worldTransform.getRotation()); - _avatar->setTargetScale(worldTransform.getScale().x); + // _avatar->setTargetScale(worldTransform.getScale().x); } From 89e5db11a016005e6b402cdd297ada002256d287 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 13 Nov 2015 17:41:41 -0800 Subject: [PATCH 0931/1003] More fixes --- examples/utilities/record/recorder.js | 13 +++++-------- libraries/avatars/src/AvatarData.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/utilities/record/recorder.js b/examples/utilities/record/recorder.js index b44cb23aa8..40476626e8 100644 --- a/examples/utilities/record/recorder.js +++ b/examples/utilities/record/recorder.js @@ -176,16 +176,13 @@ function formatTime(time) { var SEC_PER_MIN = 60; var MSEC_PER_SEC = 1000; - time = time * (MSEC_PER_SEC); + var hours = Math.floor(time / (SEC_PER_MIN * MIN_PER_HOUR)); + time -= hours * (SEC_PER_MIN * MIN_PER_HOUR); - var hours = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR)); - time -= hours * (MSEC_PER_SEC * SEC_PER_MIN * MIN_PER_HOUR); + var minutes = Math.floor(time / (SEC_PER_MIN)); + time -= minutes * (SEC_PER_MIN); - var minutes = Math.floor(time / (MSEC_PER_SEC * SEC_PER_MIN)); - time -= minutes * (MSEC_PER_SEC * SEC_PER_MIN); - - var seconds = Math.floor(time / MSEC_PER_SEC); - seconds = time / MSEC_PER_SEC; + var seconds = time; var text = ""; text += (hours > 0) ? hours + ":" : diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8537f37f84..a47d5f663e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1513,7 +1513,8 @@ void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { recordingBasis = std::make_shared(); recordingBasis->setRotation(getOrientation()); recordingBasis->setTranslation(getPosition()); - recordingBasis->setScale(getTargetScale()); + // TODO: find a different way to record/playback the Scale of the avatar + //recordingBasis->setScale(getTargetScale()); } _recordingBasis = recordingBasis; } @@ -1664,7 +1665,9 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { auto worldTransform = currentBasis->worldTransform(relativeTransform); _avatar->setPosition(worldTransform.getTranslation()); _avatar->setOrientation(worldTransform.getRotation()); - // _avatar->setTargetScale(worldTransform.getScale().x); + + // TODO: find a way to record/playback the Scale of the avatar + //_avatar->setTargetScale(worldTransform.getScale().x); } From 049fe4abee38408a3a2de76eb32734c0c0ff413a Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Fri, 13 Nov 2015 17:42:12 -0800 Subject: [PATCH 0932/1003] minor cleanup --- interface/src/avatar/MyAvatar.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fae66f5898..1deec7c51f 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -125,7 +125,7 @@ MyAvatar::MyAvatar(RigPointer rig) : AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(HEADER_NAME); }); - // FIXME how to deal with driving multiple avatars locally? + // FIXME how to deal with driving multiple avatars locally? Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::ConstPointer frame) { qDebug() << "Playback of avatar frame length: " << frame->data.size(); avatarStateFromFrame(frame->data, this); @@ -363,7 +363,6 @@ void MyAvatar::updateHMDFollowVelocity() { bool avatarIsMoving = glm::length(_velocity - _followVelocity) > FOLLOW_THRESHOLD_SPEED; bool shouldFollow = hmdIsAtRest || avatarIsMoving; - // linear part _followOffsetDistance = glm::length(offset); if (_followOffsetDistance < FOLLOW_MIN_DISTANCE) { // close enough From 245150333b949200a11bb40e8b1d840fe8e66353 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 13 Nov 2015 18:08:18 -0800 Subject: [PATCH 0933/1003] Fix for missing avatars on entry This should fix the issue where a persons avatar was occasionally not visible to others for a long period of time. This was due to several factors: 1) When a new avatar was added to the simulation that identity packet was not broadcast to other avatars on the server. This would rely on random logic to send the identity eventually. This is fixed in this PR, by sending identity packets to all other clients when a new clients arrive. 2) The random identity logic was not being executed if the sequence number check caused an update to be skipped. This means the probability of sending a random packet was reduced significantly, especially for clients that were loading geometry on entry. This was fixed by doing the random check before sequence number check. 3) The 1/300 probably used in the check was too low, this was only a 63% chance of sending a identity packet within 5 seconds. This was fixed by changing the probability to 1/187, which is a 80% chance to send an identity packet within 5 seconds. 4) The randFloat() implementation slightly reduced the identity packet probability due to quantization errors. This has been replaced by a C++ std random number generator. --- assignment-client/src/avatars/AvatarMixer.cpp | 101 +++++++++--------- .../src/avatars/AvatarMixerClientData.cpp | 14 ++- .../src/avatars/AvatarMixerClientData.h | 25 +++-- 3 files changed, 76 insertions(+), 64 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 0a455891f9..74641e9387 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -63,7 +63,9 @@ AvatarMixer::~AvatarMixer() { _broadcastThread.wait(); } -const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; +// An 80% chance of sending a identity packet within a 5 second interval. +// assuming 60 htz update rate. +const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f; // NOTE: some additional optimizations to consider. // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present @@ -243,6 +245,47 @@ void AvatarMixer::broadcastAvatarData() { return; } + // if an avatar has just connected make sure we send out the mesh and billboard + bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets() + || !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID()); + + // we will also force a send of billboard or identity packet + // if either has changed in the last frame + if (otherNodeData->getBillboardChangeTimestamp() > 0 + && (forceSend + || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp + || distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { + + QByteArray rfcUUID = otherNode->getUUID().toRfc4122(); + QByteArray billboard = otherNodeData->getAvatar().getBillboard(); + + auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size()); + billboardPacket->write(rfcUUID); + billboardPacket->write(billboard); + + nodeList->sendPacket(std::move(billboardPacket), *node); + + ++_sumBillboardPackets; + } + + if (otherNodeData->getIdentityChangeTimestamp() > 0 + && (forceSend + || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp + || distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { + + QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); + + auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); + + individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); + + identityPacket->write(individualData); + + nodeList->sendPacket(std::move(identityPacket), *node); + + ++_sumIdentityPackets; + } + AvatarData& otherAvatar = otherNodeData->getAvatar(); // Decide whether to send this avatar's data based on it's distance from us @@ -254,10 +297,10 @@ void AvatarMixer::broadcastAvatarData() { // potentially update the max full rate distance for this frame maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - if (distanceToAvatar != 0.0f + if (distanceToAvatar != 0.0f && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { - return; - } + return; + } AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); @@ -291,53 +334,11 @@ void AvatarMixer::broadcastAvatarData() { numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += - avatarPacketList->write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO)); + avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO)); avatarPacketList->endSegment(); - - // if the receiving avatar has just connected make sure we send out the mesh and billboard - // for this avatar (assuming they exist) - bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets(); - - // we will also force a send of billboard or identity packet - // if either has changed in the last frame - - if (otherNodeData->getBillboardChangeTimestamp() > 0 - && (forceSend - || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp - || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - - QByteArray rfcUUID = otherNode->getUUID().toRfc4122(); - QByteArray billboard = otherNodeData->getAvatar().getBillboard(); - - auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size()); - billboardPacket->write(rfcUUID); - billboardPacket->write(billboard); - - nodeList->sendPacket(std::move(billboardPacket), *node); - - ++_sumBillboardPackets; - } - - if (otherNodeData->getIdentityChangeTimestamp() > 0 - && (forceSend - || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp - || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - - QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); - - auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); - - individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); - - identityPacket->write(individualData); - - nodeList->sendPacket(std::move(identityPacket), *node); - - ++_sumIdentityPackets; - } }); - + // close the current packet so that we're always sending something avatarPacketList->closeCurrentPacket(true); @@ -484,7 +485,7 @@ void AvatarMixer::sendStatsPacket() { // add the key to ask the domain-server for a username replacement, if it has it avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); - + avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth(); @@ -537,7 +538,7 @@ void AvatarMixer::run() { qDebug() << "Waiting for domain settings from domain-server."; // block until we get the settingsRequestComplete signal - + QEventLoop loop; connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 0a9be20691..bfa7b99b68 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -16,7 +16,7 @@ int AvatarMixerClientData::parseData(NLPacket& packet) { // pull the sequence number from the data first packet.readPrimitive(&_lastReceivedSequenceNumber); - + // compute the offset to the data payload return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead())); } @@ -27,6 +27,14 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() { return oldValue; } +bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) { + if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) { + _hasReceivedFirstPacketsFrom.insert(uuid); + return false; + } + return true; +} + uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); @@ -45,9 +53,9 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends; - + jsonObject[OUTBOUND_AVATAR_DATA_STATS_KEY] = getOutboundAvatarDataKbps(); jsonObject[INBOUND_AVATAR_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; - + jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate(); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index e68a54c265..42a2c1d4e4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -34,25 +35,26 @@ class AvatarMixerClientData : public NodeData { public: int parseData(NLPacket& packet); AvatarData& getAvatar() { return _avatar; } - + bool checkAndSetHasReceivedFirstPackets(); + bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid); uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber) { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } - + uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; } void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; } - + quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; } - + void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } float getFullRateDistance() const { return _fullRateDistance; } - + void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; } float getMaxAvatarDistance() const { return _maxAvatarDistance; } @@ -73,31 +75,32 @@ public: void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; } void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); } - + float getOutboundAvatarDataKbps() const { return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; } - + void loadJSONStats(QJsonObject& jsonObject) const; private: AvatarData _avatar; - + uint16_t _lastReceivedSequenceNumber { 0 }; std::unordered_map _lastBroadcastSequenceNumbers; + std::unordered_set _hasReceivedFirstPacketsFrom; bool _hasReceivedFirstPackets = false; quint64 _billboardChangeTimestamp = 0; quint64 _identityChangeTimestamp = 0; - + float _fullRateDistance = FLT_MAX; float _maxAvatarDistance = FLT_MAX; - + int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; SimpleMovingAverage _otherAvatarStarves; SimpleMovingAverage _otherAvatarSkips; int _numOutOfOrderSends = 0; - + SimpleMovingAverage _avgOtherAvatarDataRate; }; From 7aefc5c9f705b42674e9a26d07704fb377626fff Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 16 Nov 2015 10:14:28 -0800 Subject: [PATCH 0934/1003] AvatarMixer: remove redundant identity packet send Removed AvatarClientData::checkAndSetHasReceivedFirstPackets boolean. This is handled by the AvatarClientData::checkAndSetHasReceivedFirstPacketsFrom set. --- assignment-client/src/avatars/AvatarMixer.cpp | 5 ++--- assignment-client/src/avatars/AvatarMixerClientData.cpp | 6 ------ assignment-client/src/avatars/AvatarMixerClientData.h | 2 -- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 74641e9387..1e17467c3b 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -245,9 +245,8 @@ void AvatarMixer::broadcastAvatarData() { return; } - // if an avatar has just connected make sure we send out the mesh and billboard - bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets() - || !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID()); + // make sure we send out identity and billboard packets to and from new arrivals. + bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID()); // we will also force a send of billboard or identity packet // if either has changed in the last frame diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index bfa7b99b68..9d78d92463 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -21,12 +21,6 @@ int AvatarMixerClientData::parseData(NLPacket& packet) { return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead())); } -bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() { - bool oldValue = _hasReceivedFirstPackets; - _hasReceivedFirstPackets = true; - return oldValue; -} - bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) { if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) { _hasReceivedFirstPacketsFrom.insert(uuid); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 42a2c1d4e4..1f5e8fa77a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -36,7 +36,6 @@ public: int parseData(NLPacket& packet); AvatarData& getAvatar() { return _avatar; } - bool checkAndSetHasReceivedFirstPackets(); bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid); uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; @@ -87,7 +86,6 @@ private: std::unordered_map _lastBroadcastSequenceNumbers; std::unordered_set _hasReceivedFirstPacketsFrom; - bool _hasReceivedFirstPackets = false; quint64 _billboardChangeTimestamp = 0; quint64 _identityChangeTimestamp = 0; From 584e35e4ab1e4fb8d28d71533bbee3b9da84a2cd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 10:58:11 -0800 Subject: [PATCH 0935/1003] have killNodeWithUUID return success to avoid double lookup --- domain-server/src/DomainServer.cpp | 4 +--- libraries/networking/src/LimitedNodeList.cpp | 5 ++++- libraries/networking/src/LimitedNodeList.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 790548c5b3..127c121cf3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1836,9 +1836,7 @@ void DomainServer::processNodeDisconnectRequestPacket(QSharedPointer p qDebug() << "Received a disconnect request from node with UUID" << nodeUUID; - if (limitedNodeList->nodeWithUUID(nodeUUID)) { - limitedNodeList->killNodeWithUUID(nodeUUID); - + if (limitedNodeList->killNodeWithUUID(nodeUUID)) { static auto removedNodePacket = NLPacket::create(PacketType::DomainServerRemovedNode, NUM_BYTES_RFC4122_UUID); removedNodePacket->reset(); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 75d42f55cb..fdb5049f00 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -441,7 +441,7 @@ void LimitedNodeList::reset() { _nodeSocket.clearConnections(); } -void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { +bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { QReadLocker readLocker(&_nodeMutex); NodeHash::iterator it = _nodeHash.find(nodeUUID); @@ -456,7 +456,10 @@ void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { } handleNodeKill(matchingNode); + return true; } + + return false; } void LimitedNodeList::processKillNode(NLPacket& packet) { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 2488b0cf8c..1aacd27572 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -230,7 +230,7 @@ public slots: virtual void sendSTUNRequest(); void sendPingPackets(); - void killNodeWithUUID(const QUuid& nodeUUID); + bool killNodeWithUUID(const QUuid& nodeUUID); signals: void dataSent(quint8 channelType, int bytes); From df05a9c8aaf72ca8c1e9a23e9294ca78ddbb9f50 Mon Sep 17 00:00:00 2001 From: "U-GAPOS\\andrew" Date: Mon, 16 Nov 2015 12:02:16 -0800 Subject: [PATCH 0936/1003] fix bug: bodySensorMatrix constantly reset when walking --- interface/src/avatar/MyAvatar.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1deec7c51f..9210dcc91c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -356,18 +356,20 @@ void MyAvatar::updateHMDFollowVelocity() { const float FOLLOW_TIMESCALE = 0.5f; const float FOLLOW_THRESHOLD_SPEED = 0.2f; - const float FOLLOW_MIN_DISTANCE = 0.02f; + const float FOLLOW_MIN_DISTANCE = 0.01f; const float FOLLOW_THRESHOLD_DISTANCE = 0.2f; + const float FOLLOW_MAX_IDLE_DISTANCE = 0.1f; bool hmdIsAtRest = _hmdAtRestDetector.update(_hmdSensorPosition, _hmdSensorOrientation); - bool avatarIsMoving = glm::length(_velocity - _followVelocity) > FOLLOW_THRESHOLD_SPEED; - bool shouldFollow = hmdIsAtRest || avatarIsMoving; _followOffsetDistance = glm::length(offset); if (_followOffsetDistance < FOLLOW_MIN_DISTANCE) { // close enough _followOffsetDistance = 0.0f; } else { + bool avatarIsMoving = glm::length(_velocity - _followVelocity) > FOLLOW_THRESHOLD_SPEED; + bool shouldFollow = (hmdIsAtRest || avatarIsMoving) && _followOffsetDistance > FOLLOW_MAX_IDLE_DISTANCE; + glm::vec3 truncatedOffset = offset; if (truncatedOffset.y < 0.0f) { truncatedOffset.y = 0.0f; From cb26fc67fc348071fe22b99069f3843dd6152410 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 13 Nov 2015 17:56:14 -0800 Subject: [PATCH 0937/1003] Move recording interface back to float/seconds --- examples/utilities/record/recorder.js | 100 +++--- interface/resources/qml/RecorderDialog.qml | 105 ++++++ .../resources/qml/controls/ButtonAwesome.qml | 21 ++ interface/resources/qml/controls/Player.qml | 89 +++++ interface/src/Application.cpp | 15 +- interface/src/avatar/Head.cpp | 5 +- interface/src/avatar/MyAvatar.cpp | 123 +------ interface/src/avatar/MyAvatar.h | 12 - interface/src/avatar/SkeletonModel.cpp | 5 +- .../scripting/RecordingScriptingInterface.cpp | 328 ++++++++++++++++++ .../scripting/RecordingScriptingInterface.h | 80 +++++ interface/src/ui/RecorderDialog.cpp | 22 ++ interface/src/ui/RecorderDialog.h | 28 ++ libraries/avatars/src/AvatarData.cpp | 212 ++--------- libraries/avatars/src/AvatarData.h | 32 +- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 4 +- libraries/recording/src/recording/Clip.cpp | 23 +- libraries/recording/src/recording/Clip.h | 18 +- libraries/recording/src/recording/Deck.cpp | 136 ++++++-- libraries/recording/src/recording/Deck.h | 44 ++- libraries/recording/src/recording/Forward.h | 10 +- libraries/recording/src/recording/Frame.cpp | 27 ++ libraries/recording/src/recording/Frame.h | 32 +- .../recording/src/recording/Recorder.cpp | 22 +- libraries/recording/src/recording/Recorder.h | 19 +- .../recording/src/recording/impl/ArrayClip.h | 100 ++++++ .../src/recording/impl/BufferClip.cpp | 87 ++--- .../recording/src/recording/impl/BufferClip.h | 23 +- .../recording/src/recording/impl/FileClip.cpp | 128 +++---- .../recording/src/recording/impl/FileClip.h | 46 +-- .../src/recording/impl/OffsetClip.cpp | 25 +- .../recording/src/recording/impl/OffsetClip.h | 14 +- .../src/recording/impl/WrapperClip.cpp | 8 +- .../src/recording/impl/WrapperClip.h | 7 +- 34 files changed, 1270 insertions(+), 680 deletions(-) create mode 100644 interface/resources/qml/RecorderDialog.qml create mode 100644 interface/resources/qml/controls/ButtonAwesome.qml create mode 100644 interface/resources/qml/controls/Player.qml create mode 100644 interface/src/scripting/RecordingScriptingInterface.cpp create mode 100644 interface/src/scripting/RecordingScriptingInterface.h create mode 100644 interface/src/ui/RecorderDialog.cpp create mode 100644 interface/src/ui/RecorderDialog.h create mode 100644 libraries/recording/src/recording/impl/ArrayClip.h diff --git a/examples/utilities/record/recorder.js b/examples/utilities/record/recorder.js index 40476626e8..d08cdd68f3 100644 --- a/examples/utilities/record/recorder.js +++ b/examples/utilities/record/recorder.js @@ -15,11 +15,11 @@ Script.include("../../libraries/toolBars.js"); var recordingFile = "recording.rec"; function setPlayerOptions() { - MyAvatar.setPlayFromCurrentLocation(true); - MyAvatar.setPlayerUseDisplayName(false); - MyAvatar.setPlayerUseAttachments(false); - MyAvatar.setPlayerUseHeadModel(false); - MyAvatar.setPlayerUseSkeletonModel(false); + Recording.setPlayFromCurrentLocation(true); + Recording.setPlayerUseDisplayName(false); + Recording.setPlayerUseAttachments(false); + Recording.setPlayerUseHeadModel(false); + Recording.setPlayerUseSkeletonModel(false); } var windowDimensions = Controller.getViewportDimensions(); @@ -64,16 +64,16 @@ function setupToolBar() { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, - alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON, + alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, visible: true - }, true, !MyAvatar.isRecording()); + }, true, !Recording.isRecording()); var playLoopWidthFactor = 1.65; playIcon = toolBar.addTool({ imageURL: TOOL_ICON_URL + "play-pause.svg", width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, - alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); @@ -82,7 +82,7 @@ function setupToolBar() { subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, - alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + alpha: (Recording.isRecording() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); @@ -93,7 +93,7 @@ function setupToolBar() { imageURL: TOOL_ICON_URL + "recording-save.svg", width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, - alpha: (MyAvatar.isRecording() || MyAvatar.isPlaying() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, + alpha: (Recording.isRecording() || Recording.isPlaying() || Recording.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); @@ -101,7 +101,7 @@ function setupToolBar() { imageURL: TOOL_ICON_URL + "recording-upload.svg", width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, - alpha: (MyAvatar.isRecording() || MyAvatar.isPlaying()) ? ALPHA_OFF : ALPHA_ON, + alpha: (Recording.isRecording() || Recording.isPlaying()) ? ALPHA_OFF : ALPHA_ON, visible: true }, false); } @@ -147,23 +147,23 @@ function setupTimer() { function updateTimer() { var text = ""; - if (MyAvatar.isRecording()) { - text = formatTime(MyAvatar.recorderElapsed()); + if (Recording.isRecording()) { + text = formatTime(Recording.recorderElapsed()); } else { - text = formatTime(MyAvatar.playerElapsed()) + " / " + - formatTime(MyAvatar.playerLength()); + text = formatTime(Recording.playerElapsed()) + " / " + + formatTime(Recording.playerLength()); } Overlays.editOverlay(timer, { text: text }) - toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing); + toolBar.changeSpacing(text.length * 8 + ((Recording.isRecording()) ? 15 : 0), spacing); - if (MyAvatar.isRecording()) { + if (Recording.isRecording()) { slider.pos = 1.0; - } else if (MyAvatar.playerLength() > 0) { - slider.pos = MyAvatar.playerElapsed() / MyAvatar.playerLength(); + } else if (Recording.playerLength() > 0) { + slider.pos = Recording.playerElapsed() / Recording.playerLength(); } Overlays.editOverlay(slider.foreground, { @@ -217,77 +217,77 @@ function moveUI() { function mousePressEvent(event) { clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - if (recordIcon === toolBar.clicked(clickedOverlay, false) && !MyAvatar.isPlaying()) { - if (!MyAvatar.isRecording()) { - MyAvatar.startRecording(); + if (recordIcon === toolBar.clicked(clickedOverlay, false) && !Recording.isPlaying()) { + if (!Recording.isRecording()) { + Recording.startRecording(); toolBar.selectTool(recordIcon, false); toolBar.setAlpha(ALPHA_OFF, playIcon); toolBar.setAlpha(ALPHA_OFF, playLoopIcon); toolBar.setAlpha(ALPHA_OFF, saveIcon); toolBar.setAlpha(ALPHA_OFF, loadIcon); } else { - MyAvatar.stopRecording(); + Recording.stopRecording(); toolBar.selectTool(recordIcon, true ); - MyAvatar.loadLastRecording(); + Recording.loadLastRecording(); toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playLoopIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); } - } else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { - if (MyAvatar.isPlaying()) { - MyAvatar.pausePlayer(); + } else if (playIcon === toolBar.clicked(clickedOverlay) && !Recording.isRecording()) { + if (Recording.isPlaying()) { + Recording.pausePlayer(); toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); - } else if (MyAvatar.playerLength() > 0) { + } else if (Recording.playerLength() > 0) { setPlayerOptions(); - MyAvatar.setPlayerLoop(false); - MyAvatar.startPlaying(); + Recording.setPlayerLoop(false); + Recording.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); toolBar.setAlpha(ALPHA_OFF, saveIcon); toolBar.setAlpha(ALPHA_OFF, loadIcon); watchStop = true; } - } else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) { - if (MyAvatar.isPlaying()) { - MyAvatar.pausePlayer(); + } else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !Recording.isRecording()) { + if (Recording.isPlaying()) { + Recording.pausePlayer(); toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); toolBar.setAlpha(ALPHA_ON, loadIcon); - } else if (MyAvatar.playerLength() > 0) { + } else if (Recording.playerLength() > 0) { setPlayerOptions(); - MyAvatar.setPlayerLoop(true); - MyAvatar.startPlaying(); + Recording.setPlayerLoop(true); + Recording.startPlaying(); toolBar.setAlpha(ALPHA_OFF, recordIcon); toolBar.setAlpha(ALPHA_OFF, saveIcon); toolBar.setAlpha(ALPHA_OFF, loadIcon); } } else if (saveIcon === toolBar.clicked(clickedOverlay)) { - if (!MyAvatar.isRecording() && !MyAvatar.isPlaying() && MyAvatar.playerLength() != 0) { + if (!Recording.isRecording() && !Recording.isPlaying() && Recording.playerLength() != 0) { recordingFile = Window.save("Save recording to file", ".", "Recordings (*.hfr)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { - MyAvatar.saveRecording(recordingFile); + Recording.saveRecording(recordingFile); } } } else if (loadIcon === toolBar.clicked(clickedOverlay)) { - if (!MyAvatar.isRecording() && !MyAvatar.isPlaying()) { + if (!Recording.isRecording() && !Recording.isPlaying()) { recordingFile = Window.browse("Load recorcding from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { - MyAvatar.loadRecording(recordingFile); + Recording.loadRecording(recordingFile); } - if (MyAvatar.playerLength() > 0) { + if (Recording.playerLength() > 0) { toolBar.setAlpha(ALPHA_ON, playIcon); toolBar.setAlpha(ALPHA_ON, playLoopIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); } } - } else if (MyAvatar.playerLength() > 0 && + } else if (Recording.playerLength() > 0 && slider.x < event.x && event.x < slider.x + slider.w && slider.y < event.y && event.y < slider.y + slider.h) { isSliding = true; slider.pos = (event.x - slider.x) / slider.w; - MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); + Recording.setPlayerTime(slider.pos * Recording.playerLength()); } } var isSliding = false; @@ -296,10 +296,10 @@ function mouseMoveEvent(event) { if (isSliding) { slider.pos = (event.x - slider.x) / slider.w; if (slider.pos < 0.0 || slider.pos > 1.0) { - MyAvatar.stopPlaying(); + Recording.stopPlaying(); slider.pos = 0.0; } - MyAvatar.setPlayerTime(slider.pos * MyAvatar.playerLength()); + Recording.setPlayerTime(slider.pos * Recording.playerLength()); } } @@ -316,7 +316,7 @@ function update() { updateTimer(); - if (watchStop && !MyAvatar.isPlaying()) { + if (watchStop && !Recording.isPlaying()) { watchStop = false; toolBar.setAlpha(ALPHA_ON, recordIcon); toolBar.setAlpha(ALPHA_ON, saveIcon); @@ -325,11 +325,11 @@ function update() { } function scriptEnding() { - if (MyAvatar.isRecording()) { - MyAvatar.stopRecording(); + if (Recording.isRecording()) { + Recording.stopRecording(); } - if (MyAvatar.isPlaying()) { - MyAvatar.stopPlaying(); + if (Recording.isPlaying()) { + Recording.stopPlaying(); } toolBar.cleanup(); Overlays.deleteOverlay(timer); diff --git a/interface/resources/qml/RecorderDialog.qml b/interface/resources/qml/RecorderDialog.qml new file mode 100644 index 0000000000..4f197846aa --- /dev/null +++ b/interface/resources/qml/RecorderDialog.qml @@ -0,0 +1,105 @@ +// +// Created by Bradley Austin Davis on 2015/11/14 +// 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 +// + + +import Hifi 1.0 +import QtQuick 2.4 +import "controls" +import "styles" + +VrDialog { + id: root + HifiConstants { id: hifi } + + property real spacing: hifi.layout.spacing + property real outerSpacing: hifi.layout.spacing * 2 + + objectName: "RecorderDialog" + + destroyOnInvisible: false + destroyOnCloseButton: false + + contentImplicitWidth: recorderDialog.width + contentImplicitHeight: recorderDialog.height + + RecorderDialog { + id: recorderDialog + x: root.clientX; y: root.clientY + width: 800 + height: 128 + signal play() + signal rewind() + + onPlay: { + console.log("Pressed play") + player.isPlaying = !player.isPlaying + } + + onRewind: { + console.log("Pressed rewind") + player.position = 0 + } + + Row { + height: 32 + ButtonAwesome { + id: cmdRecord + visible: root.showRecordButton + width: 32; height: 32 + text: "\uf111" + iconColor: "red" + onClicked: { + console.log("Pressed record") + status.text = "Recording"; + } + } + } + Text { + id: status + anchors.top: parent.top + anchors.right: parent.right + width: 128 + text: "Idle" + } + + Player { + id: player + y: root.clientY + 64 + height: 64 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + + +// onClicked: { +// if (recordTimer.running) { +// recordTimer.stop(); +// } +// recordTimer.start(); +// } + Timer { + id: recordTimer; + interval: 1000; running: false; repeat: false + onTriggered: { + console.log("Recording: " + MyAvatar.isRecording()) + MyAvatar.startRecording(); + console.log("Recording: " + MyAvatar.isRecording()) + } + } + + } + + Component.onCompleted: { + player.play.connect(play) + player.rewind.connect(rewind) + + } + } +} + diff --git a/interface/resources/qml/controls/ButtonAwesome.qml b/interface/resources/qml/controls/ButtonAwesome.qml new file mode 100644 index 0000000000..47c9fdc742 --- /dev/null +++ b/interface/resources/qml/controls/ButtonAwesome.qml @@ -0,0 +1,21 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 as OriginalStyles +import "." +import "../styles" + +Original.Button { + property color iconColor: "black" + FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; } + style: OriginalStyles.ButtonStyle { + label: Text { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: iconFont.name + font.pointSize: 20 + color: control.enabled ? control.iconColor : "gray" + text: control.text + } + } +} diff --git a/interface/resources/qml/controls/Player.qml b/interface/resources/qml/controls/Player.qml new file mode 100644 index 0000000000..8af0b1527d --- /dev/null +++ b/interface/resources/qml/controls/Player.qml @@ -0,0 +1,89 @@ +// +// AddressBarDialog.qml +// +// Created by Austin Davis on 2015/04/14 +// 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 +// + +//import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls.Styles 1.2 +import "../styles" + +Item { + id: root + + signal play() + signal rewind() + + property real duration: 100 + property real position: 50 + property bool isPlaying: false + implicitHeight: 64 + implicitWidth: 640 + + Item { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: root.height / 2 + Text { + id: labelCurrent + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: 56 + text: "00:00:00" + } + Slider { + value: root.position / root.duration + anchors.top: parent.top + anchors.topMargin: 2 + anchors.bottomMargin: 2 + anchors.bottom: parent.bottom + anchors.left: labelCurrent.right + anchors.leftMargin: 4 + anchors.right: labelDuration.left + anchors.rightMargin: 4 + } + Text { + id: labelDuration + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: 56 + text: "00:00:00" + } + } + + Row { + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + height: root.height / 2; + ButtonAwesome { + id: cmdPlay + anchors.top: parent.top + anchors.bottom: parent.bottom + text: isPlaying ? "\uf04c" : "\uf04b" + width: root.height / 2; + onClicked: root.play(); + } + ButtonAwesome { + id: cmdRewind + anchors.top: parent.top + anchors.bottom: parent.bottom + width: root.height / 2 + text: "\uf04a" + onClicked: root.rewind(); + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d54813135e..96b8ab74a8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -94,6 +94,8 @@ #include #include #include +#include +#include #include "AnimDebugDraw.h" #include "AudioClient.h" @@ -124,6 +126,7 @@ #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" +#include "scripting/RecordingScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" @@ -132,6 +135,7 @@ #endif #include "Stars.h" #include "ui/AddressBarDialog.h" +#include "ui/RecorderDialog.h" #include "ui/AvatarInputs.h" #include "ui/AssetUploadDialogFactory.h" #include "ui/DataWebDialog.h" @@ -295,6 +299,8 @@ bool setupEssentials(int& argc, char** argv) { Setting::init(); // Set dependencies + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(NodeType::Agent, listenPort); DependencyManager::set(); @@ -319,6 +325,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -996,6 +1003,7 @@ void Application::initializeGL() { void Application::initializeUi() { AddressBarDialog::registerType(); + RecorderDialog::registerType(); ErrorDialog::registerType(); LoginDialog::registerType(); MessageDialog::registerType(); @@ -1011,6 +1019,7 @@ void Application::initializeUi() { offscreenUi->load("RootMenu.qml"); auto scriptingInterface = DependencyManager::get(); offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data()); + offscreenUi->getRootContext()->setContextProperty("MyAvatar", getMyAvatar()); _glWidget->installEventFilter(offscreenUi.data()); VrMenu::load(); VrMenu::executeQueuedLambdas(); @@ -1580,8 +1589,9 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_X: if (isMeta && isShifted) { - auto offscreenUi = DependencyManager::get(); - offscreenUi->load("TestControllers.qml"); +// auto offscreenUi = DependencyManager::get(); +// offscreenUi->load("TestControllers.qml"); + RecorderDialog::toggle(); } break; @@ -3969,6 +3979,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri RayToOverlayIntersectionResultFromScriptValue); scriptEngine->registerGlobalObject("Desktop", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Recording", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 1a9b8a49e2..b8cf8ab4f1 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "Application.h" #include "Avatar.h" @@ -91,9 +92,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { if (isMine) { MyAvatar* myAvatar = static_cast(_owningAvatar); - + auto player = DependencyManager::get(); // Only use face trackers when not playing back a recording. - if (!myAvatar->isPlaying()) { + if (!player->isPlaying()) { FaceTracker* faceTracker = qApp->getActiveFaceTracker(); _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); if (_isFaceTrackerConnected) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e040ac2df9..38eb5042f7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -80,10 +80,6 @@ const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amaz const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -static const QString HEADER_NAME = "com.highfidelity.recording.AvatarData"; -static recording::FrameType AVATAR_FRAME_TYPE = recording::Frame::TYPE_INVALID; -static std::once_flag frameTypeRegistration; - MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), @@ -121,17 +117,6 @@ MyAvatar::MyAvatar(RigPointer rig) : { using namespace recording; - std::call_once(frameTypeRegistration, [] { - AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(HEADER_NAME); - }); - - // FIXME how to deal with driving multiple avatars locally? - Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::ConstPointer frame) { - qDebug() << "Playback of avatar frame length: " << frame->data.size(); - avatarStateFromFrame(frame->data, this); - }); - - for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } @@ -326,8 +311,10 @@ void MyAvatar::simulate(float deltaTime) { } // Record avatars movements. - if (_recorder && _recorder->isRecording()) { - _recorder->recordFrame(AVATAR_FRAME_TYPE, avatarStateToFrame(this)); + auto recorder = DependencyManager::get(); + if (recorder->isRecording()) { + static const recording::FrameType FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME); + recorder->recordFrame(FRAME_TYPE, toFrame(*this)); } // consider updating our billboard @@ -403,8 +390,8 @@ void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; bool inHmd = qApp->getAvatarUpdater()->isHMDMode(); - - if (isPlaying() && inHmd) { + bool playing = DependencyManager::get()->isPlaying(); + if (inHmd && playing) { return; } @@ -455,7 +442,7 @@ void MyAvatar::updateFromTrackers(float deltaTime) { Head* head = getHead(); - if (inHmd || isPlaying()) { + if (inHmd || playing) { head->setDeltaPitch(estimatedRotation.x); head->setDeltaYaw(estimatedRotation.y); head->setDeltaRoll(estimatedRotation.z); @@ -572,102 +559,6 @@ bool MyAvatar::setJointReferential(const QUuid& id, int jointIndex) { } } -bool MyAvatar::isRecording() { - if (!_recorder) { - return false; - } - if (QThread::currentThread() != thread()) { - bool result; - QMetaObject::invokeMethod(this, "isRecording", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result)); - return result; - } - return _recorder && _recorder->isRecording(); -} - -float MyAvatar::recorderElapsed() { - if (QThread::currentThread() != thread()) { - float result; - QMetaObject::invokeMethod(this, "recorderElapsed", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(float, result)); - return result; - } - if (!_recorder) { - return 0; - } - return (float)_recorder->position() / (float) MSECS_PER_SECOND; -} - -QMetaObject::Connection _audioClientRecorderConnection; - -void MyAvatar::startRecording() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection); - return; - } - - _recorder = std::make_shared(); - // connect to AudioClient's signal so we get input audio - auto audioClient = DependencyManager::get(); - _audioClientRecorderConnection = connect(audioClient.data(), &AudioClient::inputReceived, [] { - // FIXME, missing audio data handling - }); - setRecordingBasis(); - _recorder->start(); -} - -void MyAvatar::stopRecording() { - if (!_recorder) { - return; - } - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stopRecording", Qt::BlockingQueuedConnection); - return; - } - if (_recorder) { - QObject::disconnect(_audioClientRecorderConnection); - _audioClientRecorderConnection = QMetaObject::Connection(); - _recorder->stop(); - clearRecordingBasis(); - } -} - -void MyAvatar::saveRecording(const QString& filename) { - if (!_recorder) { - qCDebug(interfaceapp) << "There is no recording to save"; - return; - } - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection, - Q_ARG(QString, filename)); - return; - } - - if (_recorder) { - auto clip = _recorder->getClip(); - recording::Clip::toFile(filename, clip); - } -} - -void MyAvatar::loadLastRecording() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection); - return; - } - - if (!_recorder || !_recorder->getClip()) { - qCDebug(interfaceapp) << "There is no recording to load"; - return; - } - - if (!_player) { - _player = std::make_shared(); - } - - _player->queueClip(_recorder->getClip()); - _player->play(); -} - void MyAvatar::startAnimation(const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { if (QThread::currentThread() != thread()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 410b8a978d..16c3abec3e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -254,13 +254,6 @@ public slots: bool setModelReferential(const QUuid& id); bool setJointReferential(const QUuid& id, int jointIndex); - bool isRecording(); - float recorderElapsed(); - void startRecording(); - void stopRecording(); - void saveRecording(const QString& filename); - void loadLastRecording(); - virtual void rebuildSkeletonBody() override; bool getEnableRigAnimations() const { return _rig->getEnableRig(); } @@ -309,9 +302,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; - const recording::RecorderPointer getRecorder() const { return _recorder; } - const recording::DeckPointer getPlayer() const { return _player; } - //void beginFollowingHMD(); //bool shouldFollowHMD() const; //void followHMD(float deltaTime); @@ -358,8 +348,6 @@ private: eyeContactTarget _eyeContactTarget; - recording::RecorderPointer _recorder; - glm::vec3 _trackedHeadPosition; Setting::Handle _realWorldFieldOfView; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1347c69d61..83c8cdfcf5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -13,6 +13,7 @@ #include #include +#include #include "Application.h" #include "Avatar.h" @@ -247,8 +248,8 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } MyAvatar* myAvatar = static_cast(_owningAvatar); - if (myAvatar->isPlaying()) { - // Don't take inputs if playing back a recording. + auto player = DependencyManager::get(); + if (player->isPlaying()) { return; } diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp new file mode 100644 index 0000000000..7e0c763dfa --- /dev/null +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -0,0 +1,328 @@ +// +// Created by Bradley Austin Davis on 2015/11/13 +// 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 "RecordingScriptingInterface.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "avatar/AvatarManager.h" +#include "Application.h" +#include "InterfaceLogging.h" + +typedef int16_t AudioSample; + + +using namespace recording; + +// FIXME move to somewhere audio related? +static const QString AUDIO_FRAME_NAME = "com.highfidelity.recording.Audio"; + +RecordingScriptingInterface::RecordingScriptingInterface() { + static const recording::FrameType AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME); + // FIXME how to deal with driving multiple avatars locally? + Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this](Frame::ConstPointer frame) { + processAvatarFrame(frame); + }); + + static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AUDIO_FRAME_NAME); + Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this](Frame::ConstPointer frame) { + processAudioFrame(frame); + }); + + _player = DependencyManager::get(); + _recorder = DependencyManager::get(); + + auto audioClient = DependencyManager::get(); + connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); +} + +bool RecordingScriptingInterface::isPlaying() { + return _player->isPlaying(); +} + +bool RecordingScriptingInterface::isPaused() { + return _player->isPaused(); +} + +float RecordingScriptingInterface::playerElapsed() { + return (float)_player->position() / MSECS_PER_SECOND; +} + +float RecordingScriptingInterface::playerLength() { + return _player->length() / MSECS_PER_SECOND; +} + +void RecordingScriptingInterface::loadRecording(const QString& filename) { + using namespace recording; + + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection, + Q_ARG(QString, filename)); + return; + } + + ClipPointer clip = Clip::fromFile(filename); + if (!clip) { + qWarning() << "Unable to load clip data from " << filename; + } + _player->queueClip(clip); +} + +void RecordingScriptingInterface::startPlaying() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); + return; + } + auto myAvatar = DependencyManager::get()->getMyAvatar(); + // Playback from the current position + if (_playFromCurrentLocation) { + _dummyAvatar.setRecordingBasis(std::make_shared(myAvatar->getTransform())); + } else { + _dummyAvatar.clearRecordingBasis(); + } + _player->play(); +} + +void RecordingScriptingInterface::setPlayerVolume(float volume) { + // FIXME +} + +void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { + // FIXME +} + +void RecordingScriptingInterface::setPlayerTime(float time) { + _player->seek(time * MSECS_PER_SECOND); +} + +void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurrentLocation) { + _playFromCurrentLocation = playFromCurrentLocation; +} + +void RecordingScriptingInterface::setPlayerLoop(bool loop) { + _player->loop(loop); +} + +void RecordingScriptingInterface::setPlayerUseDisplayName(bool useDisplayName) { + _useDisplayName = useDisplayName; +} + +void RecordingScriptingInterface::setPlayerUseAttachments(bool useAttachments) { + _useAttachments = useAttachments; +} + +void RecordingScriptingInterface::setPlayerUseHeadModel(bool useHeadModel) { + _useHeadModel = useHeadModel; +} + +void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonModel) { + _useSkeletonModel = useSkeletonModel; +} + +void RecordingScriptingInterface::play() { + _player->play(); +} + +void RecordingScriptingInterface::pausePlayer() { + _player->pause(); +} + +void RecordingScriptingInterface::stopPlaying() { + _player->stop(); +} + +bool RecordingScriptingInterface::isRecording() { + return _recorder->isRecording(); +} + +float RecordingScriptingInterface::recorderElapsed() { + return _recorder->position(); +} + +void RecordingScriptingInterface::startRecording() { + if (_recorder->isRecording()) { + qCWarning(interfaceapp) << "Recorder is already running"; + return; + } + + + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection); + return; + } + + _recordingEpoch = Frame::epochForFrameTime(0); + + _audioRecordingBuffer.clear(); + auto myAvatar = DependencyManager::get()->getMyAvatar(); + myAvatar->setRecordingBasis(); + _recorder->start(); +} + +float calculateAudioTime(const QByteArray& audio) { + static const float AUDIO_BYTES_PER_SECOND = AudioConstants::SAMPLE_RATE * sizeof(AudioConstants::AudioSample); + return (float)audio.size() / AUDIO_BYTES_PER_SECOND; +} + +void injectAudioFrame(Clip::Pointer& clip, Frame::Time time, const QByteArray& audio) { + static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AUDIO_FRAME_NAME); + clip->addFrame(std::make_shared(AUDIO_FRAME_TYPE, time, audio)); +} + +// Detect too much audio in a single frame, or too much deviation between +// the expected audio length and the computed audio length +bool shouldStartNewAudioFrame(const QByteArray& currentAudioFrame, float expectedAudioLength) { + if (currentAudioFrame.isEmpty()) { + return true; + } + + // 100 milliseconds + float actualAudioLength = calculateAudioTime(currentAudioFrame); + static const float MAX_AUDIO_PACKET_DURATION = 1.0f; + if (actualAudioLength >= MAX_AUDIO_PACKET_DURATION) { + return true; + } + + + float deviation = std::abs(actualAudioLength - expectedAudioLength); + + qDebug() << "Checking buffer deviation current length "; + qDebug() << "Actual: " << actualAudioLength; + qDebug() << "Expected: " << expectedAudioLength; + qDebug() << "Deviation: " << deviation; + + static const float MAX_AUDIO_DEVIATION = 0.1f; + if (deviation >= MAX_AUDIO_PACKET_DURATION) { + return true; + } + + return false; +} + + +void injectAudioFrames(Clip::Pointer& clip, const QList>& audioBuffer) { + Frame::Time lastAudioStartTime = 0; + QByteArray audioFrameBuffer; + for (const auto& audioPacket : audioBuffer) { + float expectedAudioLength = Frame::frameTimeToSeconds(audioPacket.first - lastAudioStartTime); + if (shouldStartNewAudioFrame(audioFrameBuffer, expectedAudioLength)) { + // Time to start a new frame, inject the old one if it exists + if (audioFrameBuffer.size()) { + injectAudioFrame(clip, lastAudioStartTime, audioFrameBuffer); + audioFrameBuffer.clear(); + } + lastAudioStartTime = audioPacket.first; + } + audioFrameBuffer.append(audioPacket.second); + } +} + + +void RecordingScriptingInterface::stopRecording() { + _recorder->stop(); + + _lastClip = _recorder->getClip(); + // post-process the audio into discreet chunks based on times of received samples + injectAudioFrames(_lastClip, _audioRecordingBuffer); + _audioRecordingBuffer.clear(); + _lastClip->seek(0); + Frame::ConstPointer frame; + while (frame = _lastClip->nextFrame()) { + qDebug() << "Frame time " << frame->timeOffset << " size " << frame->data.size(); + } + _lastClip->seek(0); + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + myAvatar->clearRecordingBasis(); +} + +void RecordingScriptingInterface::saveRecording(const QString& filename) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "saveRecording", Qt::BlockingQueuedConnection, + Q_ARG(QString, filename)); + return; + } + + if (!_lastClip) { + qWarning() << "There is no recording to save"; + return; + } + + recording::Clip::toFile(filename, _lastClip); +} + +void RecordingScriptingInterface::loadLastRecording() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadLastRecording", Qt::BlockingQueuedConnection); + return; + } + + if (!_lastClip) { + qCDebug(interfaceapp) << "There is no recording to load"; + return; + } + + _player->queueClip(_lastClip); + _player->play(); +} + +void RecordingScriptingInterface::processAvatarFrame(const Frame::ConstPointer& frame) { + Q_ASSERT(QThread::currentThread() == thread()); + + AvatarData::fromFrame(frame->data, _dummyAvatar); + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + if (_useHeadModel && _dummyAvatar.getFaceModelURL().isValid() && + (_dummyAvatar.getFaceModelURL() != myAvatar->getFaceModelURL())) { + // FIXME + //myAvatar->setFaceModelURL(_dummyAvatar.getFaceModelURL()); + } + + if (_useSkeletonModel && _dummyAvatar.getSkeletonModelURL().isValid() && + (_dummyAvatar.getSkeletonModelURL() != myAvatar->getSkeletonModelURL())) { + // FIXME + //myAvatar->useFullAvatarURL() + } + + if (_useDisplayName && _dummyAvatar.getDisplayName() != myAvatar->getDisplayName()) { + myAvatar->setDisplayName(_dummyAvatar.getDisplayName()); + } + + myAvatar->setPosition(_dummyAvatar.getPosition()); + myAvatar->setOrientation(_dummyAvatar.getOrientation()); + + // FIXME attachments + // FIXME joints + // FIXME head lean + // FIXME head orientation +} + +void RecordingScriptingInterface::processAudioInput(const QByteArray& audio) { + if (_recorder->isRecording()) { + auto audioFrameTime = Frame::frameTimeFromEpoch(_recordingEpoch); + _audioRecordingBuffer.push_back({ audioFrameTime, audio }); + qDebug() << "Got sound packet of size " << audio.size() << " At time " << audioFrameTime; + } +} + +void RecordingScriptingInterface::processAudioFrame(const recording::FrameConstPointer& frame) { + AudioInjectorOptions options; + auto myAvatar = DependencyManager::get()->getMyAvatar(); + options.position = myAvatar->getPosition(); + options.orientation = myAvatar->getOrientation(); + // FIXME store the audio format (sample rate, bits, stereo) in the frame + options.stereo = false; + // FIXME move audio injector to a thread pool model? + AudioInjector::playSoundAndDelete(frame->data, options, nullptr); +} diff --git a/interface/src/scripting/RecordingScriptingInterface.h b/interface/src/scripting/RecordingScriptingInterface.h new file mode 100644 index 0000000000..f7add9480b --- /dev/null +++ b/interface/src/scripting/RecordingScriptingInterface.h @@ -0,0 +1,80 @@ +// +// Created by Bradley Austin Davis on 2015/11/13 +// 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_RecordingScriptingInterface_h +#define hifi_RecordingScriptingInterface_h + +#include + +#include + +#include +#include +#include +#include + +class RecordingScriptingInterface : public QObject, public Dependency { + Q_OBJECT + +public: + RecordingScriptingInterface(); + +public slots: + bool isPlaying(); + bool isPaused(); + float playerElapsed(); + float playerLength(); + void loadRecording(const QString& filename); + void startPlaying(); + void setPlayerVolume(float volume); + void setPlayerAudioOffset(float audioOffset); + void setPlayerTime(float time); + void setPlayFromCurrentLocation(bool playFromCurrentLocation); + void setPlayerLoop(bool loop); + void setPlayerUseDisplayName(bool useDisplayName); + void setPlayerUseAttachments(bool useAttachments); + void setPlayerUseHeadModel(bool useHeadModel); + void setPlayerUseSkeletonModel(bool useSkeletonModel); + void play(); + void pausePlayer(); + void stopPlaying(); + bool isRecording(); + float recorderElapsed(); + void startRecording(); + void stopRecording(); + void saveRecording(const QString& filename); + void loadLastRecording(); + +signals: + void playbackStateChanged(); + // Should this occur for any frame or just for seek calls? + void playbackPositionChanged(); + void looped(); + +private: + using Mutex = std::recursive_mutex; + using Locker = std::unique_lock; + using Flag = std::atomic; + void processAvatarFrame(const recording::FrameConstPointer& frame); + void processAudioFrame(const recording::FrameConstPointer& frame); + void processAudioInput(const QByteArray& audioData); + QSharedPointer _player; + QSharedPointer _recorder; + QList> _audioRecordingBuffer; + quint64 _recordingEpoch { 0 }; + + Flag _playFromCurrentLocation { true }; + Flag _useDisplayName { false }; + Flag _useHeadModel { false }; + Flag _useAttachments { false }; + Flag _useSkeletonModel { false }; + recording::ClipPointer _lastClip; + AvatarData _dummyAvatar; +}; + +#endif // hifi_RecordingScriptingInterface_h diff --git a/interface/src/ui/RecorderDialog.cpp b/interface/src/ui/RecorderDialog.cpp new file mode 100644 index 0000000000..ddefa9fbd9 --- /dev/null +++ b/interface/src/ui/RecorderDialog.cpp @@ -0,0 +1,22 @@ +// +// Created by Bradley Austin Davis on 2015/11/14 +// 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 "RecorderDialog.h" + +#include + +#include "DependencyManager.h" + +HIFI_QML_DEF(RecorderDialog) + +RecorderDialog::RecorderDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { +} + +void RecorderDialog::hide() { + ((QQuickItem*)parent())->setEnabled(false); +} diff --git a/interface/src/ui/RecorderDialog.h b/interface/src/ui/RecorderDialog.h new file mode 100644 index 0000000000..f4f0a7c2d8 --- /dev/null +++ b/interface/src/ui/RecorderDialog.h @@ -0,0 +1,28 @@ +// +// Created by Bradley Austin Davis on 2015/11/14 +// 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_RecorderDialog_h +#define hifi_RecorderDialog_h + +#include + +class RecorderDialog : public OffscreenQmlDialog { + Q_OBJECT + HIFI_QML_DECL + +public: + RecorderDialog(QQuickItem* parent = nullptr); + +signals: + +protected: + void hide(); +}; + +#endif diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a47d5f663e..017ef7578a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -33,8 +33,7 @@ #include #include #include -#include -#include +#include #include "AvatarLogging.h" @@ -45,6 +44,9 @@ using namespace std; const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f); const glm::vec3 DEFAULT_LOCAL_AABOX_SCALE(1.0f); +const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData"; +static std::once_flag frameTypeRegistration; + AvatarData::AvatarData() : _sessionUUID(), _position(0.0f), @@ -791,155 +793,10 @@ bool AvatarData::hasReferential() { return _referential != NULL; } -bool AvatarData::isPlaying() { - return _player && _player->isPlaying(); -} - -bool AvatarData::isPaused() { - return _player && _player->isPaused(); -} - -float AvatarData::playerElapsed() { - if (!_player) { - return 0; - } - if (QThread::currentThread() != thread()) { - float result; - QMetaObject::invokeMethod(this, "playerElapsed", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(float, result)); - return result; - } - return (float)_player->position() / (float) MSECS_PER_SECOND; -} - -float AvatarData::playerLength() { - if (!_player) { - return 0; - } - if (QThread::currentThread() != thread()) { - float result; - QMetaObject::invokeMethod(this, "playerLength", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(float, result)); - return result; - } - return (float)_player->length() / (float) MSECS_PER_SECOND; -} - -void AvatarData::loadRecording(const QString& filename) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadRecording", Qt::BlockingQueuedConnection, - Q_ARG(QString, filename)); - return; - } - using namespace recording; - - ClipPointer clip = Clip::fromFile(filename); - if (!clip) { - qWarning() << "Unable to load clip data from " << filename; - } - - _player = std::make_shared(); - _player->queueClip(clip); -} - -void AvatarData::startPlaying() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); - return; - } - - if (!_player) { - qWarning() << "No clip loaded for playback"; - return; - } - setRecordingBasis(); - _player->play(); -} - -void AvatarData::setPlayerVolume(float volume) { - // FIXME -} - -void AvatarData::setPlayerAudioOffset(float audioOffset) { - // FIXME -} - -void AvatarData::setPlayerTime(float time) { - if (!_player) { - qWarning() << "No player active"; - return; - } - - _player->seek(time * MSECS_PER_SECOND); -} - -void AvatarData::setPlayFromCurrentLocation(bool playFromCurrentLocation) { - // FIXME -} - -void AvatarData::setPlayerLoop(bool loop) { - if (_player) { - _player->loop(loop); - } -} - -void AvatarData::setPlayerUseDisplayName(bool useDisplayName) { - // FIXME -} - -void AvatarData::setPlayerUseAttachments(bool useAttachments) { - // FIXME -} - -void AvatarData::setPlayerUseHeadModel(bool useHeadModel) { - // FIXME -} - -void AvatarData::setPlayerUseSkeletonModel(bool useSkeletonModel) { - // FIXME -} - -void AvatarData::play() { - if (isPlaying()) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "play", Qt::BlockingQueuedConnection); - return; - } - - _player->play(); - } -} - std::shared_ptr AvatarData::getRecordingBasis() const { return _recordingBasis; } -void AvatarData::pausePlayer() { - if (!_player) { - return; - } - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection); - return; - } - if (_player) { - _player->pause(); - } -} - -void AvatarData::stopPlaying() { - if (!_player) { - return; - } - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection); - return; - } - if (_player) { - _player->stop(); - } -} - void AvatarData::changeReferential(Referential* ref) { delete _referential; _referential = ref; @@ -1568,26 +1425,26 @@ JointData jointDataFromJsonValue(const QJsonValue& json) { // This allows the application to decide whether playback should be relative to an avatar's // transform at the start of playback, or relative to the transform of the recorded // avatar -QByteArray avatarStateToFrame(const AvatarData* _avatar) { +QByteArray AvatarData::toFrame(const AvatarData& avatar) { QJsonObject root; - if (!_avatar->getFaceModelURL().isEmpty()) { - root[JSON_AVATAR_HEAD_MODEL] = _avatar->getFaceModelURL().toString(); + if (!avatar.getFaceModelURL().isEmpty()) { + root[JSON_AVATAR_HEAD_MODEL] = avatar.getFaceModelURL().toString(); } - if (!_avatar->getSkeletonModelURL().isEmpty()) { - root[JSON_AVATAR_BODY_MODEL] = _avatar->getSkeletonModelURL().toString(); + if (!avatar.getSkeletonModelURL().isEmpty()) { + root[JSON_AVATAR_BODY_MODEL] = avatar.getSkeletonModelURL().toString(); } - if (!_avatar->getDisplayName().isEmpty()) { - root[JSON_AVATAR_DISPLAY_NAME] = _avatar->getDisplayName(); + if (!avatar.getDisplayName().isEmpty()) { + root[JSON_AVATAR_DISPLAY_NAME] = avatar.getDisplayName(); } - if (!_avatar->getAttachmentData().isEmpty()) { + if (!avatar.getAttachmentData().isEmpty()) { // FIXME serialize attachment data } - auto recordingBasis = _avatar->getRecordingBasis(); + auto recordingBasis = avatar.getRecordingBasis(); if (recordingBasis) { // Find the relative transform - auto relativeTransform = recordingBasis->relativeTransform(_avatar->getTransform()); + auto relativeTransform = recordingBasis->relativeTransform(avatar.getTransform()); // if the resulting relative basis is identity, we shouldn't record anything if (!relativeTransform.isIdentity()) { @@ -1595,17 +1452,17 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) { root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); } } else { - root[JSON_AVATAR_RELATIVE] = Transform::toJson(_avatar->getTransform()); + root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatar.getTransform()); } // Skeleton pose QJsonArray jointArray; - for (const auto& joint : _avatar->getRawJointData()) { + for (const auto& joint : avatar.getRawJointData()) { jointArray.push_back(toJsonValue(joint)); } root[JSON_AVATAR_JOINT_ARRAY] = jointArray; - const HeadData* head = _avatar->getHeadData(); + const HeadData* head = avatar.getHeadData(); if (head) { QJsonObject headJson; QJsonArray blendshapeCoefficients; @@ -1616,8 +1473,8 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) { headJson[JSON_AVATAR_HEAD_ROTATION] = toJsonValue(head->getRawOrientation()); headJson[JSON_AVATAR_HEAD_LEAN_FORWARD] = QJsonValue(head->getLeanForward()); headJson[JSON_AVATAR_HEAD_LEAN_SIDEWAYS] = QJsonValue(head->getLeanSideways()); - vec3 relativeLookAt = glm::inverse(_avatar->getOrientation()) * - (head->getLookAtPosition() - _avatar->getPosition()); + vec3 relativeLookAt = glm::inverse(avatar.getOrientation()) * + (head->getLookAtPosition() - avatar.getPosition()); headJson[JSON_AVATAR_HEAD_LOOKAT] = toJsonValue(relativeLookAt); root[JSON_AVATAR_HEAD] = headJson; } @@ -1625,26 +1482,29 @@ QByteArray avatarStateToFrame(const AvatarData* _avatar) { return QJsonDocument(root).toBinaryData(); } -void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { +void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { QJsonDocument doc = QJsonDocument::fromBinaryData(frameData); QJsonObject root = doc.object(); if (root.contains(JSON_AVATAR_HEAD_MODEL)) { auto faceModelURL = root[JSON_AVATAR_HEAD_MODEL].toString(); - if (faceModelURL != _avatar->getFaceModelURL().toString()) { - _avatar->setFaceModelURL(faceModelURL); + if (faceModelURL != result.getFaceModelURL().toString()) { + QUrl faceModel(faceModelURL); + if (faceModel.isValid()) { + result.setFaceModelURL(faceModel); + } } } if (root.contains(JSON_AVATAR_BODY_MODEL)) { auto bodyModelURL = root[JSON_AVATAR_BODY_MODEL].toString(); - if (bodyModelURL != _avatar->getSkeletonModelURL().toString()) { - _avatar->setSkeletonModelURL(bodyModelURL); + if (bodyModelURL != result.getSkeletonModelURL().toString()) { + result.setSkeletonModelURL(bodyModelURL); } } if (root.contains(JSON_AVATAR_DISPLAY_NAME)) { auto newDisplayName = root[JSON_AVATAR_DISPLAY_NAME].toString(); - if (newDisplayName != _avatar->getDisplayName()) { - _avatar->setDisplayName(newDisplayName); + if (newDisplayName != result.getDisplayName()) { + result.setDisplayName(newDisplayName); } } @@ -1656,18 +1516,18 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { // The first is more useful for playing back recordings on your own avatar, while // the latter is more useful for playing back other avatars within your scene. - auto currentBasis = _avatar->getRecordingBasis(); + auto currentBasis = result.getRecordingBasis(); if (!currentBasis) { currentBasis = std::make_shared(Transform::fromJson(root[JSON_AVATAR_BASIS])); } auto relativeTransform = Transform::fromJson(root[JSON_AVATAR_RELATIVE]); auto worldTransform = currentBasis->worldTransform(relativeTransform); - _avatar->setPosition(worldTransform.getTranslation()); - _avatar->setOrientation(worldTransform.getRotation()); + result.setPosition(worldTransform.getTranslation()); + result.setOrientation(worldTransform.getRotation()); // TODO: find a way to record/playback the Scale of the avatar - //_avatar->setTargetScale(worldTransform.getScale().x); + //result.setTargetScale(worldTransform.getScale().x); } @@ -1689,13 +1549,13 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { for (const auto& joint : jointArray) { jointRotations.push_back(joint.rotation); } - _avatar->setJointRotations(jointRotations); + result.setJointRotations(jointRotations); } #if 0 // Most head data is relative to the avatar, and needs no basis correction, // but the lookat vector does need correction - HeadData* head = _avatar->_headData; + HeadData* head = result._headData; if (head && root.contains(JSON_AVATAR_HEAD)) { QJsonObject headJson = root[JSON_AVATAR_HEAD].toObject(); if (headJson.contains(JSON_AVATAR_HEAD_BLENDSHAPE_COEFFICIENTS)) { @@ -1718,7 +1578,7 @@ void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar) { if (headJson.contains(JSON_AVATAR_HEAD_LOOKAT)) { auto relativeLookAt = vec3FromJsonValue(headJson[JSON_AVATAR_HEAD_LOOKAT]); if (glm::length2(relativeLookAt) > 0.01) { - head->setLookAtPosition((_avatar->getOrientation() * relativeLookAt) + _avatar->getPosition()); + head->setLookAtPosition((result.getOrientation() * relativeLookAt) + result.getPosition()); } } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 26bc9d83ff..e79c0be80a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -50,13 +50,12 @@ typedef unsigned long long quint64; #include #include #include +#include #include "AABox.h" #include "HandData.h" #include "HeadData.h" #include "PathUtils.h" -#include "Player.h" -#include "Recorder.h" #include "Referential.h" using AvatarSharedPointer = std::shared_ptr; @@ -165,7 +164,13 @@ class AvatarData : public QObject { Q_PROPERTY(QStringList jointNames READ getJointNames) Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) + public: + static const QString FRAME_NAME; + + static void fromFrame(const QByteArray& frameData, AvatarData& avatar); + static QByteArray toFrame(const AvatarData& avatar); + AvatarData(); virtual ~AvatarData(); @@ -348,25 +353,6 @@ public slots: void setJointMappingsFromNetworkReply(); void setSessionUUID(const QUuid& sessionUUID) { _sessionUUID = sessionUUID; } bool hasReferential(); - - bool isPlaying(); - bool isPaused(); - float playerElapsed(); - float playerLength(); - void loadRecording(const QString& filename); - void startPlaying(); - void setPlayerVolume(float volume); - void setPlayerAudioOffset(float audioOffset); - void setPlayerTime(float time); - void setPlayFromCurrentLocation(bool playFromCurrentLocation); - void setPlayerLoop(bool loop); - void setPlayerUseDisplayName(bool useDisplayName); - void setPlayerUseAttachments(bool useAttachments); - void setPlayerUseHeadModel(bool useHeadModel); - void setPlayerUseSkeletonModel(bool useSkeletonModel); - void play(); - void pausePlayer(); - void stopPlaying(); protected: QUuid _sessionUUID; @@ -421,8 +407,6 @@ protected: QWeakPointer _owningAvatarMixer; - recording::DeckPointer _player; - /// Loads the joint indices, names from the FST file (if any) virtual void updateJointMappings(); void changeReferential(Referential* ref); @@ -437,7 +421,7 @@ protected: QMutex avatarLock; // Name is redundant, but it aids searches. // During recording, this holds the starting position, orientation & scale of the recorded avatar - // During playback, it holds the + // During playback, it holds the origin from which to play the relative positions in the clip TransformPointer _recordingBasis; private: diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index b6603deb62..e8a950a16b 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -26,7 +26,7 @@ #include "OffscreenGlCanvas.h" // FIXME move to threaded rendering with Qt 5.5 -// #define QML_THREADED +//#define QML_THREADED // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to @@ -72,7 +72,7 @@ public: OffscreenGlCanvas::create(shareContext); #ifdef QML_THREADED // Qt 5.5 - // _renderControl->prepareThread(_renderThread); + _renderControl->prepareThread(_renderThread); _context->moveToThread(&_thread); moveToThread(&_thread); _thread.setObjectName("QML Thread"); diff --git a/libraries/recording/src/recording/Clip.cpp b/libraries/recording/src/recording/Clip.cpp index 28e4211fe3..abe66ccb2e 100644 --- a/libraries/recording/src/recording/Clip.cpp +++ b/libraries/recording/src/recording/Clip.cpp @@ -23,7 +23,7 @@ Clip::Pointer Clip::fromFile(const QString& filePath) { return result; } -void Clip::toFile(const QString& filePath, Clip::Pointer clip) { +void Clip::toFile(const QString& filePath, const Clip::ConstPointer& clip) { FileClip::write(filePath, clip->duplicate()); } @@ -31,19 +31,10 @@ Clip::Pointer Clip::newClip() { return std::make_shared(); } -Clip::Pointer Clip::duplicate() { - Clip::Pointer result = std::make_shared(); - - Locker lock(_mutex); - Time currentPosition = position(); - seek(0); - - auto frame = nextFrame(); - while (frame) { - result->addFrame(frame); - frame = nextFrame(); - } - - seek(currentPosition); - return result; +void Clip::seek(float offset) { + seekFrameTime(Frame::secondsToFrameTime(offset)); } + +float Clip::position() const { + return Frame::frameTimeToSeconds(positionFrameTime()); +}; diff --git a/libraries/recording/src/recording/Clip.h b/libraries/recording/src/recording/Clip.h index a00ab72c98..722fadf0b2 100644 --- a/libraries/recording/src/recording/Clip.h +++ b/libraries/recording/src/recording/Clip.h @@ -16,6 +16,8 @@ #include +#include "Frame.h" + class QIODevice; namespace recording { @@ -23,16 +25,22 @@ namespace recording { class Clip { public: using Pointer = std::shared_ptr; + using ConstPointer = std::shared_ptr; virtual ~Clip() {} - Pointer duplicate(); + virtual Pointer duplicate() const = 0; - virtual Time duration() const = 0; + virtual QString getName() const = 0; + + virtual float duration() const = 0; virtual size_t frameCount() const = 0; - virtual void seek(Time offset) = 0; - virtual Time position() const = 0; + virtual void seek(float offset) final; + virtual float position() const final; + + virtual void seekFrameTime(Frame::Time offset) = 0; + virtual Frame::Time positionFrameTime() const = 0; virtual FrameConstPointer peekFrame() const = 0; virtual FrameConstPointer nextFrame() = 0; @@ -40,7 +48,7 @@ public: virtual void addFrame(FrameConstPointer) = 0; static Pointer fromFile(const QString& filePath); - static void toFile(const QString& filePath, Pointer clip); + static void toFile(const QString& filePath, const ConstPointer& clip); static Pointer newClip(); protected: diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index 10209c26d7..e52fcc16e6 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -14,31 +14,46 @@ #include "Clip.h" #include "Frame.h" #include "Logging.h" +#include "impl/OffsetClip.h" using namespace recording; -void Deck::queueClip(ClipPointer clip, Time timeOffset) { +Deck::Deck(QObject* parent) + : QObject(parent) {} + +void Deck::queueClip(ClipPointer clip, float timeOffset) { + Locker lock(_mutex); + if (!clip) { qCWarning(recordingLog) << "Clip invalid, ignoring"; return; } - // FIXME if the time offset is not zero, wrap the clip in a OffsetClip wrapper + // FIXME disabling multiple clips for now + _clips.clear(); + + // if the time offset is not zero, wrap in an OffsetClip + if (timeOffset != 0.0f) { + clip = std::make_shared(clip, timeOffset); + } + _clips.push_back(clip); _length = std::max(_length, clip->duration()); } void Deck::play() { + Locker lock(_mutex); if (_pause) { _pause = false; - _startEpoch = usecTimestampNow() - (_position * USECS_PER_MSEC); + _startEpoch = Frame::epochForFrameTime(_position); emit playbackStateChanged(); processFrames(); } } void Deck::pause() { + Locker lock(_mutex); if (!_pause) { _pause = true; emit playbackStateChanged(); @@ -47,9 +62,9 @@ void Deck::pause() { Clip::Pointer Deck::getNextClip() { Clip::Pointer result; - Time soonestFramePosition = INVALID_TIME; + auto soonestFramePosition = Frame::INVALID_TIME; for (const auto& clip : _clips) { - Time nextFramePosition = clip->position(); + auto nextFramePosition = clip->positionFrameTime(); if (nextFramePosition < soonestFramePosition) { result = clip; soonestFramePosition = nextFramePosition; @@ -58,11 +73,16 @@ Clip::Pointer Deck::getNextClip() { return result; } -void Deck::seek(Time position) { - _position = position; - // FIXME reset the frames to the appropriate spot +void Deck::seek(float position) { + Locker lock(_mutex); + _position = Frame::secondsToFrameTime(position); + + // Recompute the start epoch + _startEpoch = Frame::epochForFrameTime(_position); + + // reset the clips to the appropriate spot for (auto& clip : _clips) { - clip->seek(position); + clip->seekFrameTime(_position); } if (!_pause) { @@ -71,35 +91,46 @@ void Deck::seek(Time position) { } } -Time Deck::position() const { - if (_pause) { - return _position; +float Deck::position() const { + Locker lock(_mutex); + auto currentPosition = _position; + if (!_pause) { + currentPosition = Frame::frameTimeFromEpoch(_startEpoch); } - return (usecTimestampNow() - _startEpoch) / USECS_PER_MSEC; + return Frame::frameTimeToSeconds(currentPosition); } -static const Time MIN_FRAME_WAIT_INTERVAL_MS = 1; +static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f); +static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.002f); void Deck::processFrames() { + Locker lock(_mutex); if (_pause) { return; } - _position = position(); - auto triggerPosition = _position + MIN_FRAME_WAIT_INTERVAL_MS; + auto startingPosition = Frame::frameTimeFromEpoch(_startEpoch); + auto triggerPosition = startingPosition + MIN_FRAME_WAIT_INTERVAL; Clip::Pointer nextClip; + // FIXME add code to start dropping frames if we fall behind. + // Alternatively, add code to cache frames here and then process only the last frame of a given type + // ... the latter will work for Avatar, but not well for audio I suspect. for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) { - // If the clip is too far in the future, just break out of the handling loop - Time framePosition = nextClip->position(); - if (framePosition > triggerPosition) { + auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch); + if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) { + qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early"; break; } + // If the clip is too far in the future, just break out of the handling loop + Frame::Time framePosition = nextClip->positionFrameTime(); + if (framePosition > triggerPosition) { + break; + } // Handle the frame and advance the clip Frame::handleFrame(nextClip->nextFrame()); } - if (!nextClip) { qCDebug(recordingLog) << "No more frames available"; // No more frames available, so handle the end of playback @@ -107,6 +138,9 @@ void Deck::processFrames() { qCDebug(recordingLog) << "Looping enabled, seeking back to beginning"; // If we have looping enabled, start the playback over seek(0); + // FIXME configure the recording scripting interface to reset the avatar basis on a loop + // if doing relative movement + emit looped(); } else { // otherwise pause playback pause(); @@ -115,9 +149,67 @@ void Deck::processFrames() { } // If we have more clip frames available, set the timer for the next one - Time nextClipPosition = nextClip->position(); - Time interval = nextClipPosition - _position; + _position = Frame::frameTimeFromEpoch(_startEpoch); + auto nextFrameTime = nextClip->positionFrameTime(); + auto interval = Frame::frameTimeToMilliseconds(nextFrameTime - _position); _timer.singleShot(interval, [this] { processFrames(); }); } + +void Deck::removeClip(const ClipConstPointer& clip) { + Locker lock(_mutex); + std::remove_if(_clips.begin(), _clips.end(), [&](const Clip::ConstPointer& testClip)->bool { + return (clip == testClip); + }); +} + +void Deck::removeClip(const QString& clipName) { + Locker lock(_mutex); + std::remove_if(_clips.begin(), _clips.end(), [&](const Clip::ConstPointer& clip)->bool { + return (clip->getName() == clipName); + }); +} + +void Deck::removeAllClips() { + Locker lock(_mutex); + _clips.clear(); +} + +Deck::ClipList Deck::getClips(const QString& clipName) const { + Locker lock(_mutex); + ClipList result = _clips; + return result; +} + + +bool Deck::isPlaying() { + Locker lock(_mutex); + return !_pause; +} + +bool Deck::isPaused() const { + Locker lock(_mutex); + return _pause; +} + +void Deck::stop() { + Locker lock(_mutex); + pause(); + seek(0.0f); +} + +float Deck::length() const { + Locker lock(_mutex); + return _length; +} + +void Deck::loop(bool enable) { + Locker lock(_mutex); + _loop = enable; +} + +bool Deck::isLooping() const { + Locker lock(_mutex); + return _loop; +} diff --git a/libraries/recording/src/recording/Deck.h b/libraries/recording/src/recording/Deck.h index 7086e9759d..1f8d58d5e1 100644 --- a/libraries/recording/src/recording/Deck.h +++ b/libraries/recording/src/recording/Deck.h @@ -12,56 +12,70 @@ #include #include +#include #include #include +#include + +#include #include "Forward.h" +#include "Frame.h" namespace recording { -class Deck : public QObject { +class Deck : public QObject, public ::Dependency { Q_OBJECT public: + using ClipList = std::list; using Pointer = std::shared_ptr; - Deck(QObject* parent = nullptr) : QObject(parent) {} + + Deck(QObject* parent = nullptr); // Place a clip on the deck for recording or playback - void queueClip(ClipPointer clip, Time timeOffset = 0.0f); + void queueClip(ClipPointer clip, float timeOffset = 0.0f); + void removeClip(const ClipConstPointer& clip); + void removeClip(const QString& clipName); + void removeAllClips(); + ClipList getClips(const QString& clipName) const; void play(); - bool isPlaying() { return !_pause; } + bool isPlaying(); void pause(); - bool isPaused() const { return _pause; } + bool isPaused() const; - void stop() { pause(); seek(0.0f); } + void stop(); - Time length() const { return _length; } + float length() const; - void loop(bool enable = true) { _loop = enable; } - bool isLooping() const { return _loop; } + void loop(bool enable = true); + bool isLooping() const; - Time position() const; - void seek(Time position); + float position() const; + void seek(float position); signals: void playbackStateChanged(); + void looped(); private: - using Clips = std::list; + using Mutex = std::recursive_mutex; + using Locker = std::unique_lock; ClipPointer getNextClip(); void processFrames(); + mutable Mutex _mutex; QTimer _timer; - Clips _clips; + ClipList _clips; quint64 _startEpoch { 0 }; - Time _position { 0 }; + Frame::Time _position { 0 }; bool _pause { true }; bool _loop { false }; - Time _length { 0 }; + float _length { 0 }; }; } diff --git a/libraries/recording/src/recording/Forward.h b/libraries/recording/src/recording/Forward.h index 4ba54e23a3..1bc9b31ea9 100644 --- a/libraries/recording/src/recording/Forward.h +++ b/libraries/recording/src/recording/Forward.h @@ -16,10 +16,6 @@ namespace recording { -using Time = uint32_t; - -static const Time INVALID_TIME = std::numeric_limits::max(); - using FrameType = uint16_t; using FrameSize = uint16_t; @@ -36,16 +32,14 @@ class Clip; using ClipPointer = std::shared_ptr; +using ClipConstPointer = std::shared_ptr; + // An interface for playing back clips class Deck; -using DeckPointer = std::shared_ptr; - // An interface for recording a single clip class Recorder; -using RecorderPointer = std::shared_ptr; - } #endif diff --git a/libraries/recording/src/recording/Frame.cpp b/libraries/recording/src/recording/Frame.cpp index 5b0116519f..bff85ea872 100644 --- a/libraries/recording/src/recording/Frame.cpp +++ b/libraries/recording/src/recording/Frame.cpp @@ -12,6 +12,9 @@ #include +#include +#include + using namespace recording; // FIXME move to shared @@ -73,7 +76,31 @@ using Locker = std::unique_lock; static Mutex mutex; static std::once_flag once; +float FrameHeader::frameTimeToSeconds(Frame::Time frameTime) { + float result = frameTime; + result /= MSECS_PER_SECOND; + return result; +} +uint32_t FrameHeader::frameTimeToMilliseconds(Frame::Time frameTime) { + return frameTime; +} + +Frame::Time FrameHeader::frameTimeFromEpoch(quint64 epoch) { + auto intervalMicros = (usecTimestampNow() - epoch); + intervalMicros /= USECS_PER_MSEC; + return (Frame::Time)(intervalMicros); +} + +quint64 FrameHeader::epochForFrameTime(Time frameTime) { + auto epoch = usecTimestampNow(); + epoch -= (frameTime * USECS_PER_MSEC); + return epoch; +} + +Frame::Time FrameHeader::secondsToFrameTime(float seconds) { + return (Time)(seconds * MSECS_PER_SECOND); +} FrameType Frame::registerFrameType(const QString& frameTypeName) { Locker lock(mutex); diff --git a/libraries/recording/src/recording/Frame.h b/libraries/recording/src/recording/Frame.h index f0f53ce144..3cc999f505 100644 --- a/libraries/recording/src/recording/Frame.h +++ b/libraries/recording/src/recording/Frame.h @@ -13,26 +13,46 @@ #include "Forward.h" #include +#include #include namespace recording { -struct Frame { +struct FrameHeader { + using Time = uint32_t; + + static const Time INVALID_TIME = UINT32_MAX; + static const FrameType TYPE_INVALID = 0xFFFF; + static const FrameType TYPE_HEADER = 0x0; + + static Time secondsToFrameTime(float seconds); + static float frameTimeToSeconds(Time frameTime); + + static uint32_t frameTimeToMilliseconds(Time frameTime); + + static Time frameTimeFromEpoch(quint64 epoch); + static quint64 epochForFrameTime(Time frameTime); + + FrameType type { TYPE_INVALID }; + Time timeOffset { 0 }; // milliseconds + + FrameHeader() {} + FrameHeader(FrameType type, Time timeOffset) + : type(type), timeOffset(timeOffset) { } +}; + +struct Frame : public FrameHeader { public: using Pointer = std::shared_ptr; using ConstPointer = std::shared_ptr; using Handler = std::function; - static const FrameType TYPE_INVALID = 0xFFFF; - static const FrameType TYPE_HEADER = 0x0; - FrameType type { TYPE_INVALID }; - Time timeOffset { 0 }; // milliseconds QByteArray data; Frame() {} Frame(FrameType type, float timeOffset, const QByteArray& data) - : type(type), timeOffset(timeOffset), data(data) {} + : FrameHeader(type, timeOffset), data(data) { } static FrameType registerFrameType(const QString& frameTypeName); static QMap getFrameTypes(); diff --git a/libraries/recording/src/recording/Recorder.cpp b/libraries/recording/src/recording/Recorder.cpp index f007367cae..aae31f8ec0 100644 --- a/libraries/recording/src/recording/Recorder.cpp +++ b/libraries/recording/src/recording/Recorder.cpp @@ -16,20 +16,23 @@ using namespace recording; -Recorder::~Recorder() { +Recorder::Recorder(QObject* parent) + : QObject(parent) {} -} - -Time Recorder::position() { +float Recorder::position() { + Locker lock(_mutex); + if (_clip) { + return _clip->duration(); + } return 0.0f; } void Recorder::start() { + Locker lock(_mutex); if (!_recording) { _recording = true; - if (!_clip) { - _clip = std::make_shared(); - } + // FIXME for now just record a new clip every time + _clip = std::make_shared(); _startEpoch = usecTimestampNow(); _timer.start(); emit recordingStateChanged(); @@ -37,6 +40,7 @@ void Recorder::start() { } void Recorder::stop() { + Locker lock(_mutex); if (_recording) { _recording = false; _elapsed = _timer.elapsed(); @@ -45,14 +49,17 @@ void Recorder::stop() { } bool Recorder::isRecording() { + Locker lock(_mutex); return _recording; } void Recorder::clear() { + Locker lock(_mutex); _clip.reset(); } void Recorder::recordFrame(FrameType type, QByteArray frameData) { + Locker lock(_mutex); if (!_recording || !_clip) { return; } @@ -65,6 +72,7 @@ void Recorder::recordFrame(FrameType type, QByteArray frameData) { } ClipPointer Recorder::getClip() { + Locker lock(_mutex); return _clip; } diff --git a/libraries/recording/src/recording/Recorder.h b/libraries/recording/src/recording/Recorder.h index f8346456d4..abbc964389 100644 --- a/libraries/recording/src/recording/Recorder.h +++ b/libraries/recording/src/recording/Recorder.h @@ -10,24 +10,25 @@ #ifndef hifi_Recording_Recorder_h #define hifi_Recording_Recorder_h -#include "Forward.h" +#include #include #include +#include + +#include "Forward.h" + namespace recording { // An interface for interacting with clips, creating them by recording or // playing them back. Also serialization to and from files / network sources -class Recorder : public QObject { +class Recorder : public QObject, public Dependency { Q_OBJECT public: - using Pointer = std::shared_ptr; + Recorder(QObject* parent = nullptr); - Recorder(QObject* parent = nullptr) : QObject(parent) {} - virtual ~Recorder(); - - Time position(); + float position(); // Start recording frames void start(); @@ -49,6 +50,10 @@ signals: void recordingStateChanged(); private: + using Mutex = std::recursive_mutex; + using Locker = std::unique_lock; + + Mutex _mutex; QElapsedTimer _timer; ClipPointer _clip; quint64 _elapsed { 0 }; diff --git a/libraries/recording/src/recording/impl/ArrayClip.h b/libraries/recording/src/recording/impl/ArrayClip.h new file mode 100644 index 0000000000..10b3580228 --- /dev/null +++ b/libraries/recording/src/recording/impl/ArrayClip.h @@ -0,0 +1,100 @@ +// +// Created by Bradley Austin Davis 2015/11/05 +// 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_Recording_Impl_ArrayClip_h +#define hifi_Recording_Impl_ArrayClip_h + +#include "../Clip.h" + +#include + +namespace recording { + +template +class ArrayClip : public Clip { +public: + virtual float duration() const override { + Locker lock(_mutex); + if (_frames.empty()) { + return 0; + } + return Frame::frameTimeToSeconds((*_frames.rbegin()).timeOffset); + } + + virtual size_t frameCount() const override { + Locker lock(_mutex); + return _frames.size(); + } + + Clip::Pointer duplicate() const { + auto result = newClip(); + Locker lock(_mutex); + for (size_t i = 0; i < _frames.size(); ++i) { + result->addFrame(readFrame(i)); + } + return result; + } + + virtual void seekFrameTime(Frame::Time offset) { + Locker lock(_mutex); + auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, + [](const T& a, Frame::Time b)->bool { + return a.timeOffset < b; + } + ); + _frameIndex = itr - _frames.begin(); + } + + virtual Frame::Time positionFrameTime() const override { + Locker lock(_mutex); + Frame::Time result = Frame::INVALID_TIME; + if (_frameIndex < _frames.size()) { + result = _frames[_frameIndex].timeOffset; + } + return result; + } + + virtual FrameConstPointer peekFrame() const override { + Locker lock(_mutex); + FrameConstPointer result; + if (_frameIndex < _frames.size()) { + result = readFrame(_frameIndex); + } + return result; + } + + virtual FrameConstPointer nextFrame() override { + Locker lock(_mutex); + FrameConstPointer result; + if (_frameIndex < _frames.size()) { + result = readFrame(_frameIndex++); + } + return result; + } + + virtual void skipFrame() override { + Locker lock(_mutex); + if (_frameIndex < _frames.size()) { + ++_frameIndex; + } + } + +protected: + virtual void reset() override { + _frameIndex = 0; + } + + virtual FrameConstPointer readFrame(size_t index) const = 0; + std::vector _frames; + mutable size_t _frameIndex { 0 }; +}; + +} + +#endif diff --git a/libraries/recording/src/recording/impl/BufferClip.cpp b/libraries/recording/src/recording/impl/BufferClip.cpp index 87bbfbfef7..c40d9dd42a 100644 --- a/libraries/recording/src/recording/impl/BufferClip.cpp +++ b/libraries/recording/src/recording/impl/BufferClip.cpp @@ -8,85 +8,40 @@ #include "BufferClip.h" -#include +#include +#include #include "../Frame.h" using namespace recording; - -void BufferClip::seek(Time offset) { - Locker lock(_mutex); - auto itr = std::lower_bound(_frames.begin(), _frames.end(), offset, - [](Frame::ConstPointer a, Time b)->bool { - return a->timeOffset < b; - } - ); - _frameIndex = itr - _frames.begin(); +QString BufferClip::getName() const { + return _name; } -Time BufferClip::position() const { - Locker lock(_mutex); - Time result = INVALID_TIME; - if (_frameIndex < _frames.size()) { - result = _frames[_frameIndex]->timeOffset; - } - return result; -} - -FrameConstPointer BufferClip::peekFrame() const { - Locker lock(_mutex); - FrameConstPointer result; - if (_frameIndex < _frames.size()) { - result = _frames[_frameIndex]; - } - return result; -} - -FrameConstPointer BufferClip::nextFrame() { - Locker lock(_mutex); - FrameConstPointer result; - if (_frameIndex < _frames.size()) { - result = _frames[_frameIndex]; - ++_frameIndex; - } - return result; -} void BufferClip::addFrame(FrameConstPointer newFrame) { if (newFrame->timeOffset < 0.0f) { throw std::runtime_error("Frames may not have negative time offsets"); } - auto currentPosition = position(); - seek(newFrame->timeOffset); - { - Locker lock(_mutex); - _frames.insert(_frames.begin() + _frameIndex, newFrame); - } - seek(currentPosition); -} - -void BufferClip::skipFrame() { Locker lock(_mutex); - if (_frameIndex < _frames.size()) { - ++_frameIndex; + auto itr = std::lower_bound(_frames.begin(), _frames.end(), newFrame->timeOffset, + [](const Frame& a, Frame::Time b)->bool { + return a.timeOffset < b; + } + ); + + auto newFrameIndex = itr - _frames.begin(); + //qDebug() << "Adding frame with time offset " << newFrame->timeOffset << " @ index " << newFrameIndex; + _frames.insert(_frames.begin() + newFrameIndex, Frame(*newFrame)); +} + +// Internal only function, needs no locking +FrameConstPointer BufferClip::readFrame(size_t frameIndex) const { + FramePointer result; + if (frameIndex < _frames.size()) { + result = std::make_shared(_frames[frameIndex]); } + return result; } - -void BufferClip::reset() { - Locker lock(_mutex); - _frameIndex = 0; -} - -Time BufferClip::duration() const { - if (_frames.empty()) { - return 0; - } - return (*_frames.rbegin())->timeOffset; -} - -size_t BufferClip::frameCount() const { - return _frames.size(); -} - diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index ce81dac730..af8a64716b 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -10,33 +10,22 @@ #ifndef hifi_Recording_Impl_BufferClip_h #define hifi_Recording_Impl_BufferClip_h -#include "../Clip.h" +#include "ArrayClip.h" -#include +#include namespace recording { -class BufferClip : public Clip { +class BufferClip : public ArrayClip { public: using Pointer = std::shared_ptr; - virtual ~BufferClip() {} - - virtual Time duration() const override; - virtual size_t frameCount() const override; - - virtual void seek(Time offset) override; - virtual Time position() const override; - - virtual FrameConstPointer peekFrame() const override; - virtual FrameConstPointer nextFrame() override; - virtual void skipFrame() override; + virtual QString getName() const override; virtual void addFrame(FrameConstPointer) override; private: - virtual void reset() override; - - std::vector _frames; + virtual FrameConstPointer readFrame(size_t index) const override; + QString _name { QUuid().toString() }; mutable size_t _frameIndex { 0 }; }; diff --git a/libraries/recording/src/recording/impl/FileClip.cpp b/libraries/recording/src/recording/impl/FileClip.cpp index b8e1eb26fa..80aaac4c87 100644 --- a/libraries/recording/src/recording/impl/FileClip.cpp +++ b/libraries/recording/src/recording/impl/FileClip.cpp @@ -18,15 +18,15 @@ #include "../Frame.h" #include "../Logging.h" +#include "BufferClip.h" using namespace recording; -static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(Time) + sizeof(FrameSize); - +static const qint64 MINIMUM_FRAME_SIZE = sizeof(FrameType) + sizeof(Frame::Time) + sizeof(FrameSize); static const QString FRAME_TYPE_MAP = QStringLiteral("frameTypes"); +static const QString FRAME_COMREPSSION_FLAG = QStringLiteral("compressed"); -using FrameHeaderList = std::list; using FrameTranslationMap = QMap; FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) { @@ -49,19 +49,18 @@ FrameTranslationMap parseTranslationMap(const QJsonDocument& doc) { } -FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) { - using FrameHeader = FileClip::FrameHeader; - FrameHeaderList results; +FileFrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) { + FileFrameHeaderList results; auto current = start; auto end = current + size; // Read all the frame headers // FIXME move to Frame::readHeader? while (end - current >= MINIMUM_FRAME_SIZE) { - FrameHeader header; + FileFrameHeader header; memcpy(&(header.type), current, sizeof(FrameType)); current += sizeof(FrameType); - memcpy(&(header.timeOffset), current, sizeof(Time)); - current += sizeof(Time); + memcpy(&(header.timeOffset), current, sizeof(Frame::Time)); + current += sizeof(Frame::Time); memcpy(&(header.size), current, sizeof(FrameSize)); current += sizeof(FrameSize); header.fileOffset = current - start; @@ -72,6 +71,11 @@ FrameHeaderList parseFrameHeaders(uchar* const start, const qint64& size) { current += header.size; results.push_back(header); } + qDebug() << "Parsed source data into " << results.size() << " frames"; + int i = 0; + for (const auto& frameHeader : results) { + qDebug() << "Frame " << i++ << " time " << frameHeader.timeOffset; + } return results; } @@ -89,7 +93,7 @@ FileClip::FileClip(const QString& fileName) : _file(fileName) { return; } - FrameHeaderList parsedFrameHeaders = parseFrameHeaders(_map, size); + auto parsedFrameHeaders = parseFrameHeaders(_map, size); // Verify that at least one frame exists and that the first frame is a header if (0 == parsedFrameHeaders.size()) { @@ -110,6 +114,11 @@ FileClip::FileClip(const QString& fileName) : _file(fileName) { _fileHeader = QJsonDocument::fromBinaryData(fileHeaderData); } + // Check for compression + { + _compressed = _fileHeader.object()[FRAME_COMREPSSION_FLAG].toBool(); + } + // Find the type enum translation map and fix up the frame headers { FrameTranslationMap translationMap = parseTranslationMap(_fileHeader); @@ -120,19 +129,25 @@ FileClip::FileClip(const QString& fileName) : _file(fileName) { qDebug() << translationMap; // Update the loaded headers with the frame data - _frameHeaders.reserve(parsedFrameHeaders.size()); + _frames.reserve(parsedFrameHeaders.size()); for (auto& frameHeader : parsedFrameHeaders) { if (!translationMap.contains(frameHeader.type)) { continue; } frameHeader.type = translationMap[frameHeader.type]; - _frameHeaders.push_back(frameHeader); + _frames.push_back(frameHeader); } } + +} + + +QString FileClip::getName() const { + return _file.fileName(); } // FIXME move to frame? -bool writeFrame(QIODevice& output, const Frame& frame) { +bool writeFrame(QIODevice& output, const Frame& frame, bool compressed = true) { if (frame.type == Frame::TYPE_INVALID) { qWarning() << "Attempting to write invalid frame"; return true; @@ -142,17 +157,24 @@ bool writeFrame(QIODevice& output, const Frame& frame) { if (written != sizeof(FrameType)) { return false; } - written = output.write((char*)&(frame.timeOffset), sizeof(Time)); - if (written != sizeof(Time)) { + //qDebug() << "Writing frame with time offset " << frame.timeOffset; + written = output.write((char*)&(frame.timeOffset), sizeof(Frame::Time)); + if (written != sizeof(Frame::Time)) { return false; } - uint16_t dataSize = frame.data.size(); + QByteArray frameData = frame.data; + if (compressed) { + frameData = qCompress(frameData); + } + + uint16_t dataSize = frameData.size(); written = output.write((char*)&dataSize, sizeof(FrameSize)); if (written != sizeof(uint16_t)) { return false; } + if (dataSize != 0) { - written = output.write(frame.data); + written = output.write(frameData); if (written != dataSize) { return false; } @@ -161,7 +183,8 @@ bool writeFrame(QIODevice& output, const Frame& frame) { } bool FileClip::write(const QString& fileName, Clip::Pointer clip) { - qCDebug(recordingLog) << "Writing clip to file " << fileName; + // FIXME need to move this to a different thread + //qCDebug(recordingLog) << "Writing clip to file " << fileName << " with " << clip->frameCount() << " frames"; if (0 == clip->frameCount()) { return false; @@ -182,10 +205,14 @@ bool FileClip::write(const QString& fileName, Clip::Pointer clip) { QJsonObject rootObject; rootObject.insert(FRAME_TYPE_MAP, frameTypeObj); + // Always mark new files as compressed + rootObject.insert(FRAME_COMREPSSION_FLAG, true); QByteArray headerFrameData = QJsonDocument(rootObject).toBinaryData(); - if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }))) { + // Never compress the header frame + if (!writeFrame(outputFile, Frame({ Frame::TYPE_HEADER, 0, headerFrameData }), false)) { return false; } + } clip->seek(0); @@ -207,73 +234,24 @@ FileClip::~FileClip() { } } -void FileClip::seek(Time offset) { - Locker lock(_mutex); - auto itr = std::lower_bound(_frameHeaders.begin(), _frameHeaders.end(), offset, - [](const FrameHeader& a, Time b)->bool { - return a.timeOffset < b; - } - ); - _frameIndex = itr - _frameHeaders.begin(); -} - -Time FileClip::position() const { - Locker lock(_mutex); - Time result = INVALID_TIME; - if (_frameIndex < _frameHeaders.size()) { - result = _frameHeaders[_frameIndex].timeOffset; - } - return result; -} - -FramePointer FileClip::readFrame(uint32_t frameIndex) const { +// Internal only function, needs no locking +FrameConstPointer FileClip::readFrame(size_t frameIndex) const { FramePointer result; - if (frameIndex < _frameHeaders.size()) { + if (frameIndex < _frames.size()) { result = std::make_shared(); - const FrameHeader& header = _frameHeaders[frameIndex]; + const auto& header = _frames[frameIndex]; result->type = header.type; result->timeOffset = header.timeOffset; if (header.size) { result->data.insert(0, reinterpret_cast(_map)+header.fileOffset, header.size); + if (_compressed) { + result->data = qUncompress(result->data); + } } } return result; } -FrameConstPointer FileClip::peekFrame() const { - Locker lock(_mutex); - return readFrame(_frameIndex); -} - -FrameConstPointer FileClip::nextFrame() { - Locker lock(_mutex); - auto result = readFrame(_frameIndex); - if (_frameIndex < _frameHeaders.size()) { - ++_frameIndex; - } - return result; -} - -void FileClip::skipFrame() { - ++_frameIndex; -} - -void FileClip::reset() { - _frameIndex = 0; -} - void FileClip::addFrame(FrameConstPointer) { throw std::runtime_error("File clips are read only"); } - -Time FileClip::duration() const { - if (_frameHeaders.empty()) { - return 0; - } - return _frameHeaders.rbegin()->timeOffset; -} - -size_t FileClip::frameCount() const { - return _frameHeaders.size(); -} - diff --git a/libraries/recording/src/recording/impl/FileClip.h b/libraries/recording/src/recording/impl/FileClip.h index 18c62936c1..f103a9aca6 100644 --- a/libraries/recording/src/recording/impl/FileClip.h +++ b/libraries/recording/src/recording/impl/FileClip.h @@ -10,31 +10,34 @@ #ifndef hifi_Recording_Impl_FileClip_h #define hifi_Recording_Impl_FileClip_h -#include "../Clip.h" +#include "ArrayClip.h" + +#include #include #include -#include +#include "../Frame.h" namespace recording { -class FileClip : public Clip { +struct FileFrameHeader : public FrameHeader { + FrameType type; + Frame::Time timeOffset; + uint16_t size; + quint64 fileOffset; +}; + +using FileFrameHeaderList = std::list; + +class FileClip : public ArrayClip { public: using Pointer = std::shared_ptr; FileClip(const QString& file); virtual ~FileClip(); - virtual Time duration() const override; - virtual size_t frameCount() const override; - - virtual void seek(Time offset) override; - virtual Time position() const override; - - virtual FrameConstPointer peekFrame() const override; - virtual FrameConstPointer nextFrame() override; - virtual void skipFrame() override; + virtual QString getName() const override; virtual void addFrame(FrameConstPointer) override; const QJsonDocument& getHeader() { @@ -43,27 +46,12 @@ public: static bool write(const QString& filePath, Clip::Pointer clip); - struct FrameHeader { - FrameType type; - Time timeOffset; - uint16_t size; - quint64 fileOffset; - }; - private: - - virtual void reset() override; - - - using FrameHeaderVector = std::vector; - - FramePointer readFrame(uint32_t frameIndex) const; - + virtual FrameConstPointer readFrame(size_t index) const override; QJsonDocument _fileHeader; QFile _file; - uint32_t _frameIndex { 0 }; uchar* _map { nullptr }; - FrameHeaderVector _frameHeaders; + bool _compressed { true }; }; } diff --git a/libraries/recording/src/recording/impl/OffsetClip.cpp b/libraries/recording/src/recording/impl/OffsetClip.cpp index bccd48d6c8..afca9e0b7a 100644 --- a/libraries/recording/src/recording/impl/OffsetClip.cpp +++ b/libraries/recording/src/recording/impl/OffsetClip.cpp @@ -22,15 +22,15 @@ using namespace recording; -OffsetClip::OffsetClip(const Clip::Pointer& wrappedClip, Time offset) - : WrapperClip(wrappedClip), _offset(offset) { } +OffsetClip::OffsetClip(const Clip::Pointer& wrappedClip, float offset) + : WrapperClip(wrappedClip), _offset(Frame::secondsToFrameTime(offset)) { } -void OffsetClip::seek(Time offset) { - _wrappedClip->seek(offset - _offset); +void OffsetClip::seekFrameTime(Frame::Time offset) { + _wrappedClip->seekFrameTime(offset - _offset); } -Time OffsetClip::position() const { - return _wrappedClip->position() + _offset; +Frame::Time OffsetClip::positionFrameTime() const { + return _wrappedClip->positionFrameTime() + _offset; } FrameConstPointer OffsetClip::peekFrame() const { @@ -45,7 +45,18 @@ FrameConstPointer OffsetClip::nextFrame() { return result; } -Time OffsetClip::duration() const { +float OffsetClip::duration() const { return _wrappedClip->duration() + _offset; } +QString OffsetClip::getName() const { + return _wrappedClip->getName(); +} + +Clip::Pointer OffsetClip::duplicate() const { + return std::make_shared( + _wrappedClip->duplicate(), Frame::frameTimeToSeconds(_offset)); +} + + + diff --git a/libraries/recording/src/recording/impl/OffsetClip.h b/libraries/recording/src/recording/impl/OffsetClip.h index 1c6b005b65..40301adf59 100644 --- a/libraries/recording/src/recording/impl/OffsetClip.h +++ b/libraries/recording/src/recording/impl/OffsetClip.h @@ -18,18 +18,20 @@ class OffsetClip : public WrapperClip { public: using Pointer = std::shared_ptr; - OffsetClip(const Clip::Pointer& wrappedClip, Time offset); - virtual ~OffsetClip(); + OffsetClip(const Clip::Pointer& wrappedClip, float offset); - virtual Time duration() const override; - virtual void seek(Time offset) override; - virtual Time position() const override; + virtual QString getName() const override; + + virtual Clip::Pointer duplicate() const override; + virtual float duration() const override; + virtual void seekFrameTime(Frame::Time offset) override; + virtual Frame::Time positionFrameTime() const override; virtual FrameConstPointer peekFrame() const override; virtual FrameConstPointer nextFrame() override; protected: - const Time _offset; + const Frame::Time _offset; }; } diff --git a/libraries/recording/src/recording/impl/WrapperClip.cpp b/libraries/recording/src/recording/impl/WrapperClip.cpp index f2bbacabf1..955dd47a5e 100644 --- a/libraries/recording/src/recording/impl/WrapperClip.cpp +++ b/libraries/recording/src/recording/impl/WrapperClip.cpp @@ -22,11 +22,11 @@ using namespace recording; WrapperClip::WrapperClip(const Clip::Pointer& wrappedClip) : _wrappedClip(wrappedClip) { } -void WrapperClip::seek(Time offset) { - _wrappedClip->seek(offset); +void WrapperClip::seekFrameTime(Frame::Time offset) { + _wrappedClip->seekFrameTime(offset); } -Time WrapperClip::position() const { +Frame::Time WrapperClip::positionFrameTime() const { return _wrappedClip->position(); } @@ -50,7 +50,7 @@ void WrapperClip::addFrame(FrameConstPointer) { throw std::runtime_error("Wrapper clips are read only"); } -Time WrapperClip::duration() const { +float WrapperClip::duration() const { return _wrappedClip->duration(); } diff --git a/libraries/recording/src/recording/impl/WrapperClip.h b/libraries/recording/src/recording/impl/WrapperClip.h index 3fe013e0ed..77a484b5f7 100644 --- a/libraries/recording/src/recording/impl/WrapperClip.h +++ b/libraries/recording/src/recording/impl/WrapperClip.h @@ -24,13 +24,12 @@ public: using Pointer = std::shared_ptr; WrapperClip(const Clip::Pointer& wrappedClip); - virtual ~WrapperClip(); - virtual Time duration() const override; + virtual float duration() const override; virtual size_t frameCount() const override; - virtual void seek(Time offset) override; - virtual Time position() const override; + virtual void seekFrameTime(Frame::Time offset) override; + virtual Frame::Time positionFrameTime() const override; virtual FrameConstPointer peekFrame() const override; virtual FrameConstPointer nextFrame() override; From d099f61170859097ed649e8ae179fd462d7fc078 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 16 Nov 2015 14:57:24 -0800 Subject: [PATCH 0938/1003] Updating audio record/playback mechanism to more closely match actual audio input --- .../scripting/RecordingScriptingInterface.cpp | 78 +------------------ .../scripting/RecordingScriptingInterface.h | 1 - libraries/audio-client/src/AudioClient.cpp | 33 ++++++++ libraries/audio-client/src/AudioClient.h | 1 + 4 files changed, 38 insertions(+), 75 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 7e0c763dfa..bf585f5481 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -156,7 +156,6 @@ void RecordingScriptingInterface::startRecording() { return; } - if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "startRecording", Qt::BlockingQueuedConnection); return; @@ -164,78 +163,16 @@ void RecordingScriptingInterface::startRecording() { _recordingEpoch = Frame::epochForFrameTime(0); - _audioRecordingBuffer.clear(); auto myAvatar = DependencyManager::get()->getMyAvatar(); myAvatar->setRecordingBasis(); _recorder->start(); } -float calculateAudioTime(const QByteArray& audio) { - static const float AUDIO_BYTES_PER_SECOND = AudioConstants::SAMPLE_RATE * sizeof(AudioConstants::AudioSample); - return (float)audio.size() / AUDIO_BYTES_PER_SECOND; -} - -void injectAudioFrame(Clip::Pointer& clip, Frame::Time time, const QByteArray& audio) { - static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AUDIO_FRAME_NAME); - clip->addFrame(std::make_shared(AUDIO_FRAME_TYPE, time, audio)); -} - -// Detect too much audio in a single frame, or too much deviation between -// the expected audio length and the computed audio length -bool shouldStartNewAudioFrame(const QByteArray& currentAudioFrame, float expectedAudioLength) { - if (currentAudioFrame.isEmpty()) { - return true; - } - - // 100 milliseconds - float actualAudioLength = calculateAudioTime(currentAudioFrame); - static const float MAX_AUDIO_PACKET_DURATION = 1.0f; - if (actualAudioLength >= MAX_AUDIO_PACKET_DURATION) { - return true; - } - - - float deviation = std::abs(actualAudioLength - expectedAudioLength); - - qDebug() << "Checking buffer deviation current length "; - qDebug() << "Actual: " << actualAudioLength; - qDebug() << "Expected: " << expectedAudioLength; - qDebug() << "Deviation: " << deviation; - - static const float MAX_AUDIO_DEVIATION = 0.1f; - if (deviation >= MAX_AUDIO_PACKET_DURATION) { - return true; - } - - return false; -} - - -void injectAudioFrames(Clip::Pointer& clip, const QList>& audioBuffer) { - Frame::Time lastAudioStartTime = 0; - QByteArray audioFrameBuffer; - for (const auto& audioPacket : audioBuffer) { - float expectedAudioLength = Frame::frameTimeToSeconds(audioPacket.first - lastAudioStartTime); - if (shouldStartNewAudioFrame(audioFrameBuffer, expectedAudioLength)) { - // Time to start a new frame, inject the old one if it exists - if (audioFrameBuffer.size()) { - injectAudioFrame(clip, lastAudioStartTime, audioFrameBuffer); - audioFrameBuffer.clear(); - } - lastAudioStartTime = audioPacket.first; - } - audioFrameBuffer.append(audioPacket.second); - } -} - - void RecordingScriptingInterface::stopRecording() { _recorder->stop(); _lastClip = _recorder->getClip(); // post-process the audio into discreet chunks based on times of received samples - injectAudioFrames(_lastClip, _audioRecordingBuffer); - _audioRecordingBuffer.clear(); _lastClip->seek(0); Frame::ConstPointer frame; while (frame = _lastClip->nextFrame()) { @@ -310,19 +247,12 @@ void RecordingScriptingInterface::processAvatarFrame(const Frame::ConstPointer& void RecordingScriptingInterface::processAudioInput(const QByteArray& audio) { if (_recorder->isRecording()) { - auto audioFrameTime = Frame::frameTimeFromEpoch(_recordingEpoch); - _audioRecordingBuffer.push_back({ audioFrameTime, audio }); - qDebug() << "Got sound packet of size " << audio.size() << " At time " << audioFrameTime; + static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AUDIO_FRAME_NAME); + _recorder->recordFrame(AUDIO_FRAME_TYPE, audio); } } void RecordingScriptingInterface::processAudioFrame(const recording::FrameConstPointer& frame) { - AudioInjectorOptions options; - auto myAvatar = DependencyManager::get()->getMyAvatar(); - options.position = myAvatar->getPosition(); - options.orientation = myAvatar->getOrientation(); - // FIXME store the audio format (sample rate, bits, stereo) in the frame - options.stereo = false; - // FIXME move audio injector to a thread pool model? - AudioInjector::playSoundAndDelete(frame->data, options, nullptr); + auto audioClient = DependencyManager::get(); + audioClient->handleRecordedAudioInput(frame->data); } diff --git a/interface/src/scripting/RecordingScriptingInterface.h b/interface/src/scripting/RecordingScriptingInterface.h index f7add9480b..510a4b6898 100644 --- a/interface/src/scripting/RecordingScriptingInterface.h +++ b/interface/src/scripting/RecordingScriptingInterface.h @@ -65,7 +65,6 @@ private: void processAudioInput(const QByteArray& audioData); QSharedPointer _player; QSharedPointer _recorder; - QList> _audioRecordingBuffer; quint64 _recordingEpoch { 0 }; Flag _playFromCurrentLocation { true }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 72e47073f2..a506fe217c 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -904,6 +904,39 @@ void AudioClient::handleAudioInput() { } } +void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { + if (!_audioPacket) { + // we don't have an audioPacket yet - set that up now + _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); + } + // FIXME either discard stereo in the recording or record a stereo flag + const int numNetworkBytes = _isStereoInput + ? AudioConstants::NETWORK_FRAME_BYTES_STEREO + : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkSamples = _isStereoInput + ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO + : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + + auto nodeList = DependencyManager::get(); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + if (audioMixer && audioMixer->getActiveSocket()) { + glm::vec3 headPosition = _positionGetter(); + glm::quat headOrientation = _orientationGetter(); + quint8 isStereo = _isStereoInput ? 1 : 0; + _audioPacket->reset(); + _audioPacket->setType(PacketType::MicrophoneAudioWithEcho); + _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); + _audioPacket->writePrimitive(isStereo); + _audioPacket->writePrimitive(headPosition); + _audioPacket->writePrimitive(headOrientation); + _audioPacket->write(audio); + _stats.sentPacket(); + nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); + nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); + _outgoingAvatarAudioSequenceNumber++; + } +} + void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer) { const int numNetworkOutputSamples = inputBuffer.size() / sizeof(int16_t); const int numDeviceOutputSamples = numNetworkOutputSamples * (_outputFormat.sampleRate() * _outputFormat.channelCount()) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index e699ee9266..7d2b5a783f 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -147,6 +147,7 @@ public slots: void sendDownstreamAudioStatsPacket() { _stats.sendDownstreamAudioStatsPacket(); } void handleAudioInput(); + void handleRecordedAudioInput(const QByteArray& audio); void reset(); void audioMixerKilled(); void toggleMute(); From b3b73e8cd110293af3778f4179568035726a6552 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 17 Nov 2015 12:02:35 +1300 Subject: [PATCH 0939/1003] Fix particle aging --- libraries/entities/src/ParticleEffectEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index 263d7dce0c..06fcdb495c 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -662,7 +662,7 @@ void ParticleEffectEntityItem::stepSimulation(float deltaTime) { // move head forward _particleHeadIndex = (_particleHeadIndex + 1) % _maxParticles; } else { - float age = 1.0f - _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0 + float age = _particleLifetimes[i] / _lifespan; // 0.0 .. 1.0 updateRadius(i, age); updateColor(i, age); updateAlpha(i, age); From d42a1a721f50e5de110ba114b192cd7125dc429d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 16 Nov 2015 15:26:17 -0800 Subject: [PATCH 0940/1003] first cut at messages-mixer --- assignment-client/src/Agent.cpp | 11 +- assignment-client/src/Agent.h | 1 + assignment-client/src/AssignmentFactory.cpp | 3 + .../src/messages/MessagesMixer.cpp | 535 ++++++++++++++++++ .../src/messages/MessagesMixer.h | 58 ++ .../src/messages/MessagesMixerClientData.cpp | 55 ++ .../src/messages/MessagesMixerClientData.h | 105 ++++ .../resources/describe-settings.json | 16 + domain-server/src/DomainGatekeeper.cpp | 5 +- domain-server/src/DomainServer.cpp | 1 - .../src/DomainServerSettingsManager.cpp | 5 +- libraries/networking/src/Assignment.cpp | 4 + libraries/networking/src/Assignment.h | 2 +- libraries/networking/src/DomainHandler.cpp | 2 + libraries/networking/src/MessagesClient.cpp | 113 ++++ libraries/networking/src/MessagesClient.h | 40 ++ libraries/networking/src/NetworkLogging.cpp | 1 + libraries/networking/src/NetworkLogging.h | 1 + libraries/networking/src/Node.cpp | 1 + libraries/networking/src/NodeType.h | 1 + .../networking/src/ThreadedAssignment.cpp | 3 + libraries/networking/src/udt/PacketHeaders.h | 3 +- 22 files changed, 959 insertions(+), 7 deletions(-) create mode 100644 assignment-client/src/messages/MessagesMixer.cpp create mode 100644 assignment-client/src/messages/MessagesMixer.h create mode 100644 assignment-client/src/messages/MessagesMixerClientData.cpp create mode 100644 assignment-client/src/messages/MessagesMixerClientData.h create mode 100644 libraries/networking/src/MessagesClient.cpp create mode 100644 libraries/networking/src/MessagesClient.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 063bf24de8..0d719d6806 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -55,6 +55,7 @@ Agent::Agent(NLPacket& packet) : { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, this, "handleOctreePacket"); packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket"); + packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagePacket"); } void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode) { @@ -93,7 +94,15 @@ void Agent::handleJurisdictionPacket(QSharedPointer packet, SharedNode DependencyManager::get()->getJurisdictionListener()-> queueReceivedPacket(packet, senderNode); } -} +} + +void Agent::handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode) { + auto packetType = packet->getType(); + + if (packetType == PacketType::MessagesData) { + qDebug() << "got a messages packet"; + } +} void Agent::handleAudioPacket(QSharedPointer packet) { _receivedAudioStream.parseData(*packet); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index ab000015d5..be3a0db293 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,6 +58,7 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); + void handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode); void sendPingRequests(); void processAgentAvatarAndAudio(float deltaTime); diff --git a/assignment-client/src/AssignmentFactory.cpp b/assignment-client/src/AssignmentFactory.cpp index cacc523ebd..c4cd6821ef 100644 --- a/assignment-client/src/AssignmentFactory.cpp +++ b/assignment-client/src/AssignmentFactory.cpp @@ -17,6 +17,7 @@ #include "avatars/AvatarMixer.h" #include "entities/EntityServer.h" #include "assets/AssetServer.h" +#include "messages/MessagesMixer.h" ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) { @@ -36,6 +37,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(NLPacket& packet) { return new EntityServer(packet); case Assignment::AssetServerType: return new AssetServer(packet); + case Assignment::MessagesMixerType: + return new MessagesMixer(packet); default: return NULL; } diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp new file mode 100644 index 0000000000..70b0c1b2cf --- /dev/null +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -0,0 +1,535 @@ +// +// MessagesMixer.cpp +// assignment-client/src/messages +// +// Created by Brad hefta-Gaub on 11/16/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 +// + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "MessagesMixerClientData.h" +#include "MessagesMixer.h" + +const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; + +const int MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND = 60; +const unsigned int MESSAGES_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000; + +MessagesMixer::MessagesMixer(NLPacket& packet) : + ThreadedAssignment(packet), + _broadcastThread(), + _lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()), + _trailingSleepRatio(1.0f), + _performanceThrottlingRatio(0.0f), + _sumListeners(0), + _numStatFrames(0), + _sumBillboardPackets(0), + _sumIdentityPackets(0) +{ + // make sure we hear about node kills so we can tell the other nodes + connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); + + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); + packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesDataPacket"); +} + +MessagesMixer::~MessagesMixer() { + if (_broadcastTimer) { + _broadcastTimer->deleteLater(); + } + + _broadcastThread.quit(); + _broadcastThread.wait(); +} + +// An 80% chance of sending a identity packet within a 5 second interval. +// assuming 60 htz update rate. +const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f; + +void MessagesMixer::broadcastMessagesData() { + qDebug() << "MessagesMixer::broadcastMessagesData()..."; + + int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp; + + ++_numStatFrames; + + const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; + const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; + + const float RATIO_BACK_OFF = 0.02f; + + const int TRAILING_AVERAGE_FRAMES = 100; + int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; + + const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; + const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; + + // NOTE: The following code calculates the _performanceThrottlingRatio based on how much the messages-mixer was + // able to sleep. This will eventually be used to ask for an additional messages-mixer to help out. Currently the value + // is unused as it is assumed this should not be hit before the messages-mixer hits the desired bandwidth limit per client. + // It is reported in the domain-server stats for the messages-mixer. + + _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) + + (idleTime * CURRENT_FRAME_RATIO / (float) MESSAGES_DATA_SEND_INTERVAL_MSECS); + + float lastCutoffRatio = _performanceThrottlingRatio; + bool hasRatioChanged = false; + + if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { + if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { + // we're struggling - change our performance throttling ratio + _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); + + qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; + hasRatioChanged = true; + } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { + // we've recovered and can back off the performance throttling + _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; + + if (_performanceThrottlingRatio < 0) { + _performanceThrottlingRatio = 0; + } + + qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" + << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; + hasRatioChanged = true; + } + + if (hasRatioChanged) { + framesSinceCutoffEvent = 0; + } + } + + if (!hasRatioChanged) { + ++framesSinceCutoffEvent; + } + + auto nodeList = DependencyManager::get(); + + // setup for distributed random floating point values + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + std::uniform_real_distribution distribution; + + qDebug() << "MessagesMixer::broadcastMessagesData()... calling nodeList->eachMatchingNode()"; + + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + if (!node->getLinkedData()) { + return false; + } + if (node->getType() != NodeType::Agent) { + return false; + } + if (!node->getActiveSocket()) { + return false; + } + return true; + }, + [&](const SharedNodePointer& node) { + MessagesMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); + MutexTryLocker lock(nodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + ++_sumListeners; + + AvatarData& avatar = nodeData->getAvatar(); + glm::vec3 myPosition = avatar.getPosition(); + + // reset the internal state for correct random number distribution + distribution.reset(); + + // reset the max distance for this frame + float maxAvatarDistanceThisFrame = 0.0f; + + // reset the number of sent avatars + nodeData->resetNumAvatarsSentLastFrame(); + + // keep a counter of the number of considered avatars + int numOtherAvatars = 0; + + // keep track of outbound data rate specifically for avatar data + int numAvatarDataBytes = 0; + + // keep track of the number of other avatars held back in this frame + int numAvatarsHeldBack = 0; + + // keep track of the number of other avatar frames skipped + int numAvatarsWithSkippedFrames = 0; + + // use the data rate specifically for avatar data for FRD adjustment checks + float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); + + // Check if it is time to adjust what we send this client based on the observed + // bandwidth to this node. We do this once a second, which is also the window for + // the bandwidth reported by node->getOutboundBandwidth(); + if (nodeData->getNumFramesSinceFRDAdjustment() > MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND) { + + const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f; + const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO); + const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f)); + + // get the current full rate distance so we can work with it + float currentFullRateDistance = nodeData->getFullRateDistance(); + + if (avatarDataRateLastSecond > _maxKbpsPerNode) { + + // is the FRD greater than the farthest avatar? + // if so, before we calculate anything, set it to that distance + currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance()); + + // we're adjusting the full rate distance to target a bandwidth in the middle + // of the hysterisis gap + currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; + + nodeData->setFullRateDistance(currentFullRateDistance); + nodeData->resetNumFramesSinceFRDAdjustment(); + } else if (currentFullRateDistance < nodeData->getMaxAvatarDistance() + && avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) { + // we are constrained AND we've recovered to below the acceptable ratio + // lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap + currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; + + nodeData->setFullRateDistance(currentFullRateDistance); + nodeData->resetNumFramesSinceFRDAdjustment(); + } + } else { + nodeData->incrementNumFramesSinceFRDAdjustment(); + } + + // setup a PacketList for the avatarPackets + auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + + // this is an AGENT we have received head data from + // send back a packet with other active node data to this node + nodeList->eachMatchingNode( + [&](const SharedNodePointer& otherNode)->bool { + if (!otherNode->getLinkedData()) { + return false; + } + if (otherNode->getUUID() == node->getUUID()) { + return false; + } + + return true; + }, + [&](const SharedNodePointer& otherNode) { + ++numOtherAvatars; + + MessagesMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + MutexTryLocker lock(otherNodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + + // make sure we send out identity and billboard packets to and from new arrivals. + bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID()); + + // we will also force a send of billboard or identity packet + // if either has changed in the last frame + if (otherNodeData->getBillboardChangeTimestamp() > 0 + && (forceSend + || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp + || distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { + + QByteArray rfcUUID = otherNode->getUUID().toRfc4122(); + QByteArray billboard = otherNodeData->getAvatar().getBillboard(); + + auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size()); + billboardPacket->write(rfcUUID); + billboardPacket->write(billboard); + + nodeList->sendPacket(std::move(billboardPacket), *node); + + ++_sumBillboardPackets; + } + + if (otherNodeData->getIdentityChangeTimestamp() > 0 + && (forceSend + || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp + || distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { + + QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); + + auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); + + individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); + + identityPacket->write(individualData); + + nodeList->sendPacket(std::move(identityPacket), *node); + + ++_sumIdentityPackets; + } + + AvatarData& otherAvatar = otherNodeData->getAvatar(); + // Decide whether to send this avatar's data based on it's distance from us + + // The full rate distance is the distance at which EVERY update will be sent for this avatar + // at twice the full rate distance, there will be a 50% chance of sending this avatar's update + glm::vec3 otherPosition = otherAvatar.getPosition(); + float distanceToAvatar = glm::length(myPosition - otherPosition); + + // potentially update the max full rate distance for this frame + maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); + + if (distanceToAvatar != 0.0f + && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { + return; + } + + AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); + AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); + + if (lastSeqToReceiver > lastSeqFromSender && lastSeqToReceiver != UINT16_MAX) { + // we got out out of order packets from the sender, track it + otherNodeData->incrementNumOutOfOrderSends(); + } + + // make sure we haven't already sent this data from this sender to this receiver + // or that somehow we haven't sent + if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { + ++numAvatarsHeldBack; + return; + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + // this is a skip - we still send the packet but capture the presence of the skip so we see it happening + ++numAvatarsWithSkippedFrames; + } + + // we're going to send this avatar + + // increment the number of avatars sent to this reciever + nodeData->incrementNumAvatarsSentLastFrame(); + + // set the last sent sequence number for this sender on the receiver + nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), + otherNodeData->getLastReceivedSequenceNumber()); + + // start a new segment in the PacketList for this avatar + avatarPacketList->startSegment(); + + numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); + numAvatarDataBytes += + avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO)); + + avatarPacketList->endSegment(); + }); + + // close the current packet so that we're always sending something + avatarPacketList->closeCurrentPacket(true); + + // send the avatar data PacketList + nodeList->sendPacketList(std::move(avatarPacketList), *node); + + // record the bytes sent for other avatar data in the MessagesMixerClientData + nodeData->recordSentAvatarData(numAvatarDataBytes); + + // record the number of avatars held back this frame + nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); + nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); + + if (numOtherAvatars == 0) { + // update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send + nodeData->setMaxAvatarDistance(FLT_MAX); + } else { + nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); + } + } + ); + + qDebug() << "MessagesMixer::broadcastMessagesData()... calling nodeList->eachMatchingNode() for encode..."; + + // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so + // that we can notice differences, next time around. + nodeList->eachMatchingNode( + [&](const SharedNodePointer& otherNode)->bool { + if (!otherNode->getLinkedData()) { + return false; + } + if (otherNode->getType() != NodeType::Agent) { + return false; + } + if (!otherNode->getActiveSocket()) { + return false; + } + return true; + }, + [&](const SharedNodePointer& otherNode) { + MessagesMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + MutexTryLocker lock(otherNodeData->getMutex()); + if (!lock.isLocked()) { + return; + } + AvatarData& otherAvatar = otherNodeData->getAvatar(); + otherAvatar.doneEncoding(false); + }); + + _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); +} + +void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { + if (killedNode->getType() == NodeType::Agent + && killedNode->getLinkedData()) { + auto nodeList = DependencyManager::get(); + + // this was an avatar we were sending to other people + // send a kill packet for it to our other nodes + auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); + killPacket->write(killedNode->getUUID().toRfc4122()); + + nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); + + // we also want to remove sequence number data for this avatar on our other avatars + // so invoke the appropriate method on the MessagesMixerClientData for other avatars + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + if (!node->getLinkedData()) { + return false; + } + + if (node->getUUID() == killedNode->getUUID()) { + return false; + } + + return true; + }, + [&](const SharedNodePointer& node) { + QMetaObject::invokeMethod(node->getLinkedData(), + "removeLastBroadcastSequenceNumber", + Qt::AutoConnection, + Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); + } + ); + } +} + +void MessagesMixer::handleMessagesDataPacket(QSharedPointer packet, SharedNodePointer senderNode) { + auto nodeList = DependencyManager::get(); + nodeList->updateNodeWithDataFromPacket(packet, senderNode); +} + +void MessagesMixer::sendStatsPacket() { + QJsonObject statsObject; + statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; + statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100; + statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; + + QJsonObject messagesObject; + + auto nodeList = DependencyManager::get(); + // add stats for each listerner + nodeList->eachNode([&](const SharedNodePointer& node) { + QJsonObject messagesStats; + + const QString NODE_OUTBOUND_KBPS_STAT_KEY = "outbound_kbps"; + const QString NODE_INBOUND_KBPS_STAT_KEY = "inbound_kbps"; + + // add the key to ask the domain-server for a username replacement, if it has it + messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); + messagesStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); + messagesStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth(); + + MessagesMixerClientData* clientData = static_cast(node->getLinkedData()); + if (clientData) { + MutexTryLocker lock(clientData->getMutex()); + if (lock.isLocked()) { + clientData->loadJSONStats(messagesStats); + + // add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only + messagesStats["delta_full_vs_avatar_data_kbps"] = + messagesStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - messagesStats[OUTBOUND_MESSAGES_DATA_STATS_KEY].toDouble(); + } + } + + messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats; + }); + + statsObject["messages"] = messagesObject; + ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); + + _sumListeners = 0; + _numStatFrames = 0; +} + +void MessagesMixer::run() { + ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); + + NodeType_t owningNodeType = DependencyManager::get()->getOwnerType(); + qDebug() << "owningNodeType:" << owningNodeType; + + auto nodeList = DependencyManager::get(); + nodeList->addNodeTypeToInterestSet(NodeType::Agent); + + nodeList->linkedDataCreateCallback = [] (Node* node) { + node->setLinkedData(new MessagesMixerClientData()); + }; + + // setup the timer that will be fired on the broadcast thread + _broadcastTimer = new QTimer; + _broadcastTimer->setInterval(MESSAGES_DATA_SEND_INTERVAL_MSECS); + _broadcastTimer->moveToThread(&_broadcastThread); + + // connect appropriate signals and slots + connect(_broadcastTimer, &QTimer::timeout, this, &MessagesMixer::broadcastMessagesData, Qt::DirectConnection); + connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start())); + + // wait until we have the domain-server settings, otherwise we bail + DomainHandler& domainHandler = nodeList->getDomainHandler(); + + qDebug() << "Waiting for domain settings from domain-server."; + + // block until we get the settingsRequestComplete signal + + QEventLoop loop; + connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); + connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); + domainHandler.requestDomainSettings(); + loop.exec(); + + if (domainHandler.getSettingsObject().isEmpty()) { + qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; + setFinished(true); + return; + } + + // parse the settings to pull out the values we need + parseDomainServerSettings(domainHandler.getSettingsObject()); + + // start the broadcastThread + _broadcastThread.start(); +} + +void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { + qDebug() << "MessagesMixer::parseDomainServerSettings() domainSettings:" << domainSettings; + const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; + const QString NODE_SEND_BANDWIDTH_KEY = "max_node_send_bandwidth"; + + const float DEFAULT_NODE_SEND_BANDWIDTH = 1.0f; + QJsonValue nodeBandwidthValue = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY]; + if (!nodeBandwidthValue.isDouble()) { + qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value"; + } + + _maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA; + qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps."; +} diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h new file mode 100644 index 0000000000..d96a20dd18 --- /dev/null +++ b/assignment-client/src/messages/MessagesMixer.h @@ -0,0 +1,58 @@ +// +// MessagesMixer.h +// assignment-client/src/messages +// +// Created by Brad hefta-Gaub on 11/16/2015. +// Copyright 2015 High Fidelity, Inc. +// +// The avatar mixer receives head, hand and positional data from all connected +// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms. +// +// 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_MessagesMixer_h +#define hifi_MessagesMixer_h + +#include + +/// Handles assignments of type MessagesMixer - distribution of avatar data to various clients +class MessagesMixer : public ThreadedAssignment { + Q_OBJECT +public: + MessagesMixer(NLPacket& packet); + ~MessagesMixer(); +public slots: + /// runs the avatar mixer + void run(); + + void nodeKilled(SharedNodePointer killedNode); + + void sendStatsPacket(); + +private slots: + void handleMessagesDataPacket(QSharedPointer packet, SharedNodePointer senderNode); + +private: + void broadcastMessagesData(); + void parseDomainServerSettings(const QJsonObject& domainSettings); + + QThread _broadcastThread; + + quint64 _lastFrameTimestamp; + + float _trailingSleepRatio; + float _performanceThrottlingRatio; + + int _sumListeners; + int _numStatFrames; + int _sumBillboardPackets; + int _sumIdentityPackets; + + float _maxKbpsPerNode = 0.0f; + + QTimer* _broadcastTimer = nullptr; +}; + +#endif // hifi_MessagesMixer_h diff --git a/assignment-client/src/messages/MessagesMixerClientData.cpp b/assignment-client/src/messages/MessagesMixerClientData.cpp new file mode 100644 index 0000000000..6aa8f39c22 --- /dev/null +++ b/assignment-client/src/messages/MessagesMixerClientData.cpp @@ -0,0 +1,55 @@ +// +// MessagesMixerClientData.cpp +// assignment-client/src/messages +// +// Created by Brad hefta-Gaub on 11/16/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 +// + +#include + +#include "MessagesMixerClientData.h" + +int MessagesMixerClientData::parseData(NLPacket& packet) { + // pull the sequence number from the data first + packet.readPrimitive(&_lastReceivedSequenceNumber); + + // compute the offset to the data payload + return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead())); +} + +bool MessagesMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) { + if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) { + _hasReceivedFirstPacketsFrom.insert(uuid); + return false; + } + return true; +} + +uint16_t MessagesMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { + // return the matching PacketSequenceNumber, or the default if we don't have it + auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); + if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { + return nodeMatch->second; + } else { + return 0; + } +} + +void MessagesMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { + jsonObject["display_name"] = _avatar.getDisplayName(); + jsonObject["full_rate_distance"] = _fullRateDistance; + jsonObject["max_av_distance"] = _maxAvatarDistance; + jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame; + jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); + jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); + jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends; + + jsonObject[OUTBOUND_MESSAGES_DATA_STATS_KEY] = getOutboundAvatarDataKbps(); + jsonObject[INBOUND_MESSAGES_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; + + jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate(); +} diff --git a/assignment-client/src/messages/MessagesMixerClientData.h b/assignment-client/src/messages/MessagesMixerClientData.h new file mode 100644 index 0000000000..1667df431f --- /dev/null +++ b/assignment-client/src/messages/MessagesMixerClientData.h @@ -0,0 +1,105 @@ +// +// MessagesMixerClientData.h +// assignment-client/src/messages +// +// Created by Brad hefta-Gaub on 11/16/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 +// + +#ifndef hifi_MessagesMixerClientData_h +#define hifi_MessagesMixerClientData_h + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +const QString OUTBOUND_MESSAGES_DATA_STATS_KEY = "outbound_av_data_kbps"; +const QString INBOUND_MESSAGES_DATA_STATS_KEY = "inbound_av_data_kbps"; + +class MessagesMixerClientData : public NodeData { + Q_OBJECT +public: + int parseData(NLPacket& packet); + AvatarData& getAvatar() { return _avatar; } + + bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid); + + uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; + void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber) + { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } + Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } + + uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } + + quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; } + void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; } + + quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } + void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; } + + void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } + float getFullRateDistance() const { return _fullRateDistance; } + + void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; } + float getMaxAvatarDistance() const { return _maxAvatarDistance; } + + void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } + void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } + int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; } + + void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); } + float getAvgNumOtherAvatarStarvesPerSecond() const { return _otherAvatarStarves.getAverageSampleValuePerSecond(); } + + void recordNumOtherAvatarSkips(int numOtherAvatarSkips) { _otherAvatarSkips.updateAverage((float) numOtherAvatarSkips); } + float getAvgNumOtherAvatarSkipsPerSecond() const { return _otherAvatarSkips.getAverageSampleValuePerSecond(); } + + void incrementNumOutOfOrderSends() { ++_numOutOfOrderSends; } + + int getNumFramesSinceFRDAdjustment() const { return _numFramesSinceAdjustment; } + void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; } + void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; } + + void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); } + + float getOutboundAvatarDataKbps() const + { return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; } + + void loadJSONStats(QJsonObject& jsonObject) const; +private: + AvatarData _avatar; + + uint16_t _lastReceivedSequenceNumber { 0 }; + std::unordered_map _lastBroadcastSequenceNumbers; + std::unordered_set _hasReceivedFirstPacketsFrom; + + quint64 _billboardChangeTimestamp = 0; + quint64 _identityChangeTimestamp = 0; + + float _fullRateDistance = FLT_MAX; + float _maxAvatarDistance = FLT_MAX; + + int _numAvatarsSentLastFrame = 0; + int _numFramesSinceAdjustment = 0; + + SimpleMovingAverage _otherAvatarStarves; + SimpleMovingAverage _otherAvatarSkips; + int _numOutOfOrderSends = 0; + + SimpleMovingAverage _avgOtherAvatarDataRate; +}; + +#endif // hifi_MessagesMixerClientData_h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index e0038117f0..b2443b8bd4 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -549,6 +549,22 @@ "advanced": true } ] + }, + { + "name": "messages_mixer", + "label": "Messages Mixer", + "assignment-types": [4], + "settings": [ + { + "name": "max_node_send_bandwidth", + "type": "double", + "label": "Per-Node Bandwidth", + "help": "Desired maximum send bandwidth (in Megabits per second) to each node", + "placeholder": 1.0, + "default": 1.0, + "advanced": true + } + ] } ] } diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index d360ab4802..55f0fb2d2b 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -48,7 +48,8 @@ QUuid DomainGatekeeper::assignmentUUIDForPendingAssignment(const QUuid& tempUUID const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer - << NodeType::AssetServer; + << NodeType::AssetServer + << NodeType::MessagesMixer; void DomainGatekeeper::processConnectRequestPacket(QSharedPointer packet) { if (packet->getPayloadSize() == 0) { @@ -66,7 +67,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer pack } static const NodeSet VALID_NODE_TYPES { - NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent + NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer }; if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b5fd9f2b20..1db277f47f 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -553,7 +553,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet(static_cast(defaultedType) + 1)) { if (!excludedTypes.contains(defaultedType) - && defaultedType != Assignment::UNUSED_0 && defaultedType != Assignment::UNUSED_1 && defaultedType != Assignment::AgentType) { diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index f650089486..924e19e1fc 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -131,7 +131,8 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList appSettings.setValue(JSON_SETTINGS_VERSION_KEY, _descriptionVersion); } -QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) { +QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { + qDebug() << "DomainServerSettingsManager::valueOrDefaultValueForKeyPath() keyPath:" << keyPath; const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); if (foundValue) { @@ -226,6 +227,8 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection } QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& typeValue, bool isAuthenticated) { + qDebug() << "DomainServerSettingsManager::responseObjectForType() typeValue:" << typeValue; + QJsonObject responseObject; if (!typeValue.isEmpty() || isAuthenticated) { diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 66c6bf2a2c..5fdedeafb9 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -30,6 +30,8 @@ Assignment::Type Assignment::typeForNodeType(NodeType_t nodeType) { return Assignment::EntityServerType; case NodeType::AssetServer: return Assignment::AssetServerType; + case NodeType::MessagesMixer: + return Assignment::MessagesMixerType; default: return Assignment::AllTypes; } @@ -131,6 +133,8 @@ const char* Assignment::getTypeName() const { return "asset-server"; case Assignment::EntityServerType: return "entity-server"; + case Assignment::MessagesMixerType: + return "messages-mixer"; default: return "unknown"; } diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index ee3d9cb5fd..9639411eec 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -30,7 +30,7 @@ public: AvatarMixerType = 1, AgentType = 2, AssetServerType = 3, - UNUSED_0 = 4, + MessagesMixerType = 4, UNUSED_1 = 5, EntityServerType = 6, AllTypes = 7 diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index df024b361d..9f411c59f1 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -242,6 +242,8 @@ void DomainHandler::requestDomainSettings() { Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get()->getOwnerType()); + qCDebug(networking) << "Requesting settings from domain server for assignmentType:" << assignmentType; + auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false); packet->writePrimitive(assignmentType); diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp new file mode 100644 index 0000000000..d3d44b6fdc --- /dev/null +++ b/libraries/networking/src/MessagesClient.cpp @@ -0,0 +1,113 @@ +// +// MessagesClient.cpp +// libraries/networking/src +// +// Created by Brad hefta-Gaub on 11/16/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 +// + +#include "MessagesClient.h" + +#include + +#include +#include +#include +#include + +#include "AssetRequest.h" +#include "AssetUpload.h" +#include "AssetUtils.h" +#include "NetworkAccessManager.h" +#include "NetworkLogging.h" +#include "NodeList.h" +#include "PacketReceiver.h" +#include "ResourceCache.h" + +MessagesClient::MessagesClient() { + + setCustomDeleter([](Dependency* dependency){ + static_cast(dependency)->deleteLater(); + }); + + auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); + + packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagePacket"); + + connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &MessagesClient::handleNodeKilled); +} + +void MessagesClient::init() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection); + } + + // Setup disk cache if not already + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + if (!networkAccessManager.cache()) { + QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache"; + + QNetworkDiskCache* cache = new QNetworkDiskCache(); + cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); + cache->setCacheDirectory(cachePath); + networkAccessManager.setCache(cache); + qCDebug(asset_client) << "MessagesClient disk cache setup at" << cachePath + << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; + } +} + +bool haveMessagesMixer() { + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (!messagesMixer) { + qCWarning(messages_client) << "Could not complete MessagesClient operation " + << "since you are not currently connected to a messages-mixer."; + return false; + } + + return true; +} + +void MessagesClient::handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode) { + auto packetType = packet->getType(); + + if (packetType == PacketType::MessagesData) { + qDebug() << "got a messages packet"; + } +} + +void MessagesClient::sendMessage(const QString& channel, const QString& message) { + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (messagesMixer) { + auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); + + #if 0 + auto messageID = ++_currentID; + packetList->writePrimitive(messageID); + + packetList->writePrimitive(static_cast(extension.length())); + packetList->write(extension.toLatin1().constData(), extension.length()); + + uint64_t size = data.length(); + packetList->writePrimitive(size); + packetList->write(data.constData(), size); + + nodeList->sendPacketList(std::move(packetList), *assetServer); + #endif + } +} + +void MessagesClient::handleNodeKilled(SharedNodePointer node) { + if (node->getType() != NodeType::MessagesMixer) { + return; + } + +} diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h new file mode 100644 index 0000000000..f1f13bfe20 --- /dev/null +++ b/libraries/networking/src/MessagesClient.h @@ -0,0 +1,40 @@ +// +// MessagesClient.h +// libraries/networking/src +// +// Created by Brad hefta-Gaub on 11/16/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 +// + + +#ifndef hifi_MessagesClient_h +#define hifi_MessagesClient_h + +#include + +#include + +#include "LimitedNodeList.h" +#include "NLPacket.h" +#include "Node.h" + +class MessagesClient : public QObject, public Dependency { + Q_OBJECT +public: + MessagesClient(); + + Q_INVOKABLE void init(); + + Q_INVOKABLE void sendMessage(const QString& channel, const QString& message); + +private slots: + void handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode); + void handleNodeKilled(SharedNodePointer node); + +private: +}; + +#endif diff --git a/libraries/networking/src/NetworkLogging.cpp b/libraries/networking/src/NetworkLogging.cpp index 28b209960e..01abd6ae19 100644 --- a/libraries/networking/src/NetworkLogging.cpp +++ b/libraries/networking/src/NetworkLogging.cpp @@ -13,3 +13,4 @@ Q_LOGGING_CATEGORY(networking, "hifi.networking") Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client") +Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client") diff --git a/libraries/networking/src/NetworkLogging.h b/libraries/networking/src/NetworkLogging.h index 838bbb57d2..37ebc1933d 100644 --- a/libraries/networking/src/NetworkLogging.h +++ b/libraries/networking/src/NetworkLogging.h @@ -16,5 +16,6 @@ Q_DECLARE_LOGGING_CATEGORY(networking) Q_DECLARE_LOGGING_CATEGORY(asset_client) +Q_DECLARE_LOGGING_CATEGORY(messages_client) #endif // hifi_NetworkLogging_h diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 5fea670dd0..243dca78e2 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -32,6 +32,7 @@ void NodeType::init() { TypeNameHash.insert(NodeType::Agent, "Agent"); TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer"); TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer"); + TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer"); TypeNameHash.insert(NodeType::AssetServer, "Asset Server"); TypeNameHash.insert(NodeType::Unassigned, "Unassigned"); } diff --git a/libraries/networking/src/NodeType.h b/libraries/networking/src/NodeType.h index e680f218db..d4377f4610 100644 --- a/libraries/networking/src/NodeType.h +++ b/libraries/networking/src/NodeType.h @@ -23,6 +23,7 @@ namespace NodeType { const NodeType_t AudioMixer = 'M'; const NodeType_t AvatarMixer = 'W'; const NodeType_t AssetServer = 'A'; + const NodeType_t MessagesMixer = 'm'; const NodeType_t Unassigned = 1; void init(); diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 0422c03297..992b3be2b4 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -119,9 +119,12 @@ void ThreadedAssignment::stopSendingStats() { } void ThreadedAssignment::checkInWithDomainServerOrExit() { + qDebug() << "ThreadedAssignment::checkInWithDomainServerOrExit()...."; if (DependencyManager::get()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "ThreadedAssignment::checkInWithDomainServerOrExit().... getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS"; setFinished(true); } else { + qDebug() << "ThreadedAssignment::checkInWithDomainServerOrExit().... calling DependencyManager::get()->sendDomainServerCheckIn()"; DependencyManager::get()->sendDomainServerCheckIn(); } } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 82d905bf28..099e842c27 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -79,7 +79,8 @@ enum class PacketType : uint8_t { AssetUpload, AssetUploadReply, AssetGetInfo, - AssetGetInfoReply + AssetGetInfoReply, + MessagesData }; const int NUM_BYTES_MD5_HASH = 16; From f17af601ab5dbe550238ad6231750bdb10b45d89 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 15:52:48 -0800 Subject: [PATCH 0941/1003] update BUILD for cmake version change --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 5abb3ae4e7..a24524af6f 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,6 +1,6 @@ ###Dependencies -* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2 +* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2 * [Qt](http://www.qt.io/download-open-source) ~> 5.4.1 * [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. From d2b8ba740abb14b80d15f04868db31778182ed97 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 15:59:52 -0800 Subject: [PATCH 0942/1003] change BUILD guides for 5.5.1 and homebrew recs --- BUILD.md | 15 +++++++-------- BUILD_ANDROID.md | 14 +++++++------- BUILD_OSX.md | 13 ++++--------- BUILD_WIN.md | 10 +++++----- 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/BUILD.md b/BUILD.md index a24524af6f..c1ccd3193e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,7 +1,7 @@ ###Dependencies * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2 -* [Qt](http://www.qt.io/download-open-source) ~> 5.4.1 +* [Qt](http://www.qt.io/download-open-source) ~> 5.5.1 * [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) @@ -21,10 +21,10 @@ * [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3 * [soxr](http://soxr.sourceforge.net) ~> 0.1.1 * [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3 -* [Sixense](http://sixense.com/) ~> 071615 +* [Sixense](http://sixense.com/) ~> 071615 * [zlib](http://www.zlib.net/) ~> 1.28 (Win32 only) -The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project. +The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project. These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder. @@ -42,12 +42,12 @@ Hifi uses CMake to generate build files and project files for your platform. ####Qt In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation. -For example, a Qt5 5.4.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). +For example, a Qt5 5.5.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). The path it needs to be set to will depend on where and how Qt5 was installed. e.g. - export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/clang_64/lib/cmake/ - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake + export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/clang_64/lib/cmake/ + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake ####Generating build files @@ -64,7 +64,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation: - cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/lib/cmake + cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/lib/cmake ####Finding Dependencies @@ -83,4 +83,3 @@ In the examples below the variable $NAME would be replaced by the name of the de ####Devices You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device. - diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index 9f86e7e925..88294f3040 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -14,7 +14,7 @@ You will need the following tools to build our Android targets. * Install the latest Platform-tools * Install the latest Build-tools * Install the SDK Platform for API Level 19 - * Install Sources for Android SDK for API Level 19 + * Install Sources for Android SDK for API Level 19 * Install the ARM EABI v7a System Image if you want to run an emulator. You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine. @@ -25,7 +25,7 @@ You will also need to cross-compile the dependencies required for all platforms ####ANDROID_LIB_DIR -Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies. +Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies. This is most easily accomplished by installing all Android dependencies in the same folder. You can place this folder wherever you like on your machine. In this build guide and across our CMakeLists files this folder is referred to as `ANDROID_LIB_DIR`. You can set `ANDROID_LIB_DIR` in your environment or by passing when you run CMake. @@ -45,7 +45,7 @@ The original instructions to compile OpenSSL for Android from your host environm Download the [OpenSSL source](https://www.openssl.org/source/) and extract the tarball inside your `ANDROID_LIB_DIR`. Rename the extracted folder to `openssl`. -You will need the [setenv-android.sh script](http://wiki.openssl.org/index.php/File:Setenv-android.sh) from the OpenSSL wiki. +You will need the [setenv-android.sh script](http://wiki.openssl.org/index.php/File:Setenv-android.sh) from the OpenSSL wiki. You must change three values at the top of the `setenv-android.sh` script - `_ANDROID_NDK`, `_ANDROID_EABI` and `_ANDROID_API`. `_ANDROID_NDK` should be `android-ndk-r10`, `_ANDROID_EABI` should be `arm-linux-androidebi-4.9` and `_ANDROID_API` should be `19`. @@ -62,8 +62,8 @@ source setenv-android.sh Then, from the OpenSSL directory, run the following commands. ``` -perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org -./config shared -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine --openssldir=/usr/local/ssl/$ANDROID_API +perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org +./config shared -no-ssl2 -no-ssl3 -no-comp -no-hw -no-engine --openssldir=/usr/local/ssl/$ANDROID_API make depend make all ``` @@ -78,7 +78,7 @@ The Oculus Mobile SDK is optional, for Gear VR support. It is not required to co Download the [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) and extract the archive inside your `ANDROID_LIB_DIR` folder. Rename the extracted folder to `libovr`. -From the VRLib directory, use ndk-build to build VrLib. +From the VRLib directory, use ndk-build to build VrLib. ``` cd VRLib @@ -107,4 +107,4 @@ The following must be set in your environment: The following must be passed to CMake when it is run: -* USE_ANDROID_TOOLCHAIN - set to true to build for Android \ No newline at end of file +* USE_ANDROID_TOOLCHAIN - set to true to build for Android diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 9d1357d672..54360ad4b8 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -3,20 +3,15 @@ Please read the [general build guide](BUILD.md) for information on dependencies ###Homebrew [Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple. - brew tap highfidelity/homebrew-formulas - brew install cmake openssl - brew install highfidelity/formulas/qt5 - brew link qt5 --force + brew install cmake openssl qt5 -We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository. - -*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.4.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.* +We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x and above provide a mechanism to disable the wireless scanning we previously had a custom patch for. ###Qt -Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.4.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. +Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake ###Xcode If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 89646e99ff..48781ca34a 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -16,7 +16,7 @@ If using Visual Studio 2013 and building as a Visual Studio 2013 project you nee ####nmake -Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required. +Some of the external projects may require nmake to compile and install. If it is not installed at the location listed below, please ensure that it is in your PATH so CMake can find it when required. We expect nmake.exe to be located at the following path. @@ -29,19 +29,19 @@ NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit * [Download the online installer](http://qt-project.org/downloads) * When it asks you to select components, ONLY select the following: - * Qt > Qt 5.4.1 > **msvc2013 32-bit OpenGL** + * Qt > Qt 5.5.1 > **msvc2013 32-bit** -* [Download the offline installer](http://download.qt.io/official_releases/qt/5.4/5.4.1/qt-opensource-windows-x86-msvc2013_opengl-5.4.1.exe) +* [Download the offline installer](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe) Once Qt is installed, you need to manually configure the following: -* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.4.1\msvc2013_opengl\lib\cmake` directory. +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` directory. * You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New ###External Libraries As it stands, Hifi/Interface is a 32-bit application, so all libraries should also be 32-bit. -CMake will need to know where the headers and libraries for required external dependencies are. +CMake will need to know where the headers and libraries for required external dependencies are. We use CMake's `fixup_bundle` to find the DLLs all of our exectuable targets require, and then copy them beside the executable in a post-build step. If `fixup_bundle` is having problems finding a DLL, you can fix it manually on your end by adding the folder containing that DLL to your path. Let us know which DLL CMake had trouble finding, as it is possible a tweak to our CMake files is required. From 8a27a2fba51c8a778cc234ac6bf1df2b3a1e3441 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 16 Nov 2015 16:29:10 -0800 Subject: [PATCH 0943/1003] Fixing recording interface times --- interface/src/scripting/RecordingScriptingInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index bf585f5481..32bd6fde97 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -56,11 +56,11 @@ bool RecordingScriptingInterface::isPaused() { } float RecordingScriptingInterface::playerElapsed() { - return (float)_player->position() / MSECS_PER_SECOND; + return _player->position(); } float RecordingScriptingInterface::playerLength() { - return _player->length() / MSECS_PER_SECOND; + return _player->length(); } void RecordingScriptingInterface::loadRecording(const QString& filename) { @@ -103,7 +103,7 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { } void RecordingScriptingInterface::setPlayerTime(float time) { - _player->seek(time * MSECS_PER_SECOND); + _player->seek(time); } void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurrentLocation) { From b6c27588b6e741e8b820b7791cd24cb83791954a Mon Sep 17 00:00:00 2001 From: EdgarPironti Date: Mon, 16 Nov 2015 16:50:47 -0800 Subject: [PATCH 0944/1003] ControlledAC.js refactoring --- examples/acScripts/ControlledAC.js | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/acScripts/ControlledAC.js b/examples/acScripts/ControlledAC.js index 41a8a2b257..cbc5f64b8f 100644 --- a/examples/acScripts/ControlledAC.js +++ b/examples/acScripts/ControlledAC.js @@ -38,11 +38,11 @@ var SHOW = 4; var HIDE = 5; var LOAD = 6; -Avatar.setPlayFromCurrentLocation(playFromCurrentLocation); -Avatar.setPlayerUseDisplayName(useDisplayName); -Avatar.setPlayerUseAttachments(useAttachments); -Avatar.setPlayerUseHeadModel(false); -Avatar.setPlayerUseSkeletonModel(useAvatarModel); +Recording.setPlayFromCurrentLocation(playFromCurrentLocation); +Recording.setPlayerUseDisplayName(useDisplayName); +Recording.setPlayerUseAttachments(useAttachments); +Recording.setPlayerUseHeadModel(false); +Recording.setPlayerUseSkeletonModel(useAvatarModel); function setupEntityViewer() { var entityViewerOffset = 10; @@ -96,25 +96,25 @@ function update(event) { if (!Agent.isAvatar) { Agent.isAvatar = true; } - if (!Avatar.isPlaying()) { - Avatar.startPlaying(); + if (!Recording.isPlaying()) { + Recording.startPlaying(); } - Avatar.setPlayerLoop(false); + Recording.setPlayerLoop(false); break; case PLAY_LOOP: print("Play loop"); if (!Agent.isAvatar) { Agent.isAvatar = true; } - if (!Avatar.isPlaying()) { - Avatar.startPlaying(); + if (!Recording.isPlaying()) { + Recording.startPlaying(); } - Avatar.setPlayerLoop(true); + Recording.setPlayerLoop(true); break; case STOP: print("Stop"); - if (Avatar.isPlaying()) { - Avatar.stopPlaying(); + if (Recording.isPlaying()) { + Recording.stopPlaying(); } break; case SHOW: @@ -125,15 +125,15 @@ function update(event) { break; case HIDE: print("Hide"); - if (Avatar.isPlaying()) { - Avatar.stopPlaying(); + if (Recording.isPlaying()) { + Recording.stopPlaying(); } Agent.isAvatar = false; break; case LOAD: print("Load"); if(clip_url !== null) { - Avatar.loadRecording(clip_url); + Recording.loadRecording(clip_url); } break; case DO_NOTHING: @@ -143,8 +143,8 @@ function update(event) { break; } - if (Avatar.isPlaying()) { - Avatar.play(); + if (Recording.isPlaying()) { + Recording.play(); } } From 12f206e2f04bdac6f3c980069f4e6037e51b611b Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 16 Nov 2015 17:00:03 -0800 Subject: [PATCH 0945/1003] more work on messages --- .../src/messages/MessagesMixer.cpp | 364 ++---------------- .../src/messages/MessagesMixer.h | 2 +- interface/src/Application.cpp | 11 + libraries/networking/src/MessagesClient.cpp | 25 +- libraries/networking/src/MessagesClient.h | 2 +- 5 files changed, 47 insertions(+), 357 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 70b0c1b2cf..6177850532 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -48,7 +48,7 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesDataPacket"); + packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket"); } MessagesMixer::~MessagesMixer() { @@ -64,341 +64,13 @@ MessagesMixer::~MessagesMixer() { // assuming 60 htz update rate. const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f; -void MessagesMixer::broadcastMessagesData() { - qDebug() << "MessagesMixer::broadcastMessagesData()..."; - - int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp; - - ++_numStatFrames; - - const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f; - const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f; - - const float RATIO_BACK_OFF = 0.02f; - - const int TRAILING_AVERAGE_FRAMES = 100; - int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES; - - const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES; - const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO; - - // NOTE: The following code calculates the _performanceThrottlingRatio based on how much the messages-mixer was - // able to sleep. This will eventually be used to ask for an additional messages-mixer to help out. Currently the value - // is unused as it is assumed this should not be hit before the messages-mixer hits the desired bandwidth limit per client. - // It is reported in the domain-server stats for the messages-mixer. - - _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio) - + (idleTime * CURRENT_FRAME_RATIO / (float) MESSAGES_DATA_SEND_INTERVAL_MSECS); - - float lastCutoffRatio = _performanceThrottlingRatio; - bool hasRatioChanged = false; - - if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) { - if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) { - // we're struggling - change our performance throttling ratio - _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio)); - - qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; - hasRatioChanged = true; - } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) { - // we've recovered and can back off the performance throttling - _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF; - - if (_performanceThrottlingRatio < 0) { - _performanceThrottlingRatio = 0; - } - - qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was" - << lastCutoffRatio << "and is now" << _performanceThrottlingRatio; - hasRatioChanged = true; - } - - if (hasRatioChanged) { - framesSinceCutoffEvent = 0; - } - } - - if (!hasRatioChanged) { - ++framesSinceCutoffEvent; - } - - auto nodeList = DependencyManager::get(); - - // setup for distributed random floating point values - std::random_device randomDevice; - std::mt19937 generator(randomDevice()); - std::uniform_real_distribution distribution; - - qDebug() << "MessagesMixer::broadcastMessagesData()... calling nodeList->eachMatchingNode()"; - - nodeList->eachMatchingNode( - [&](const SharedNodePointer& node)->bool { - if (!node->getLinkedData()) { - return false; - } - if (node->getType() != NodeType::Agent) { - return false; - } - if (!node->getActiveSocket()) { - return false; - } - return true; - }, - [&](const SharedNodePointer& node) { - MessagesMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); - MutexTryLocker lock(nodeData->getMutex()); - if (!lock.isLocked()) { - return; - } - ++_sumListeners; - - AvatarData& avatar = nodeData->getAvatar(); - glm::vec3 myPosition = avatar.getPosition(); - - // reset the internal state for correct random number distribution - distribution.reset(); - - // reset the max distance for this frame - float maxAvatarDistanceThisFrame = 0.0f; - - // reset the number of sent avatars - nodeData->resetNumAvatarsSentLastFrame(); - - // keep a counter of the number of considered avatars - int numOtherAvatars = 0; - - // keep track of outbound data rate specifically for avatar data - int numAvatarDataBytes = 0; - - // keep track of the number of other avatars held back in this frame - int numAvatarsHeldBack = 0; - - // keep track of the number of other avatar frames skipped - int numAvatarsWithSkippedFrames = 0; - - // use the data rate specifically for avatar data for FRD adjustment checks - float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); - - // Check if it is time to adjust what we send this client based on the observed - // bandwidth to this node. We do this once a second, which is also the window for - // the bandwidth reported by node->getOutboundBandwidth(); - if (nodeData->getNumFramesSinceFRDAdjustment() > MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND) { - - const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f; - const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO); - const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f)); - - // get the current full rate distance so we can work with it - float currentFullRateDistance = nodeData->getFullRateDistance(); - - if (avatarDataRateLastSecond > _maxKbpsPerNode) { - - // is the FRD greater than the farthest avatar? - // if so, before we calculate anything, set it to that distance - currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance()); - - // we're adjusting the full rate distance to target a bandwidth in the middle - // of the hysterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - - nodeData->setFullRateDistance(currentFullRateDistance); - nodeData->resetNumFramesSinceFRDAdjustment(); - } else if (currentFullRateDistance < nodeData->getMaxAvatarDistance() - && avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) { - // we are constrained AND we've recovered to below the acceptable ratio - // lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - - nodeData->setFullRateDistance(currentFullRateDistance); - nodeData->resetNumFramesSinceFRDAdjustment(); - } - } else { - nodeData->incrementNumFramesSinceFRDAdjustment(); - } - - // setup a PacketList for the avatarPackets - auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - - // this is an AGENT we have received head data from - // send back a packet with other active node data to this node - nodeList->eachMatchingNode( - [&](const SharedNodePointer& otherNode)->bool { - if (!otherNode->getLinkedData()) { - return false; - } - if (otherNode->getUUID() == node->getUUID()) { - return false; - } - - return true; - }, - [&](const SharedNodePointer& otherNode) { - ++numOtherAvatars; - - MessagesMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - MutexTryLocker lock(otherNodeData->getMutex()); - if (!lock.isLocked()) { - return; - } - - // make sure we send out identity and billboard packets to and from new arrivals. - bool forceSend = !otherNodeData->checkAndSetHasReceivedFirstPacketsFrom(node->getUUID()); - - // we will also force a send of billboard or identity packet - // if either has changed in the last frame - if (otherNodeData->getBillboardChangeTimestamp() > 0 - && (forceSend - || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp - || distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - - QByteArray rfcUUID = otherNode->getUUID().toRfc4122(); - QByteArray billboard = otherNodeData->getAvatar().getBillboard(); - - auto billboardPacket = NLPacket::create(PacketType::AvatarBillboard, rfcUUID.size() + billboard.size()); - billboardPacket->write(rfcUUID); - billboardPacket->write(billboard); - - nodeList->sendPacket(std::move(billboardPacket), *node); - - ++_sumBillboardPackets; - } - - if (otherNodeData->getIdentityChangeTimestamp() > 0 - && (forceSend - || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp - || distribution(generator) < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { - - QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); - - auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); - - individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); - - identityPacket->write(individualData); - - nodeList->sendPacket(std::move(identityPacket), *node); - - ++_sumIdentityPackets; - } - - AvatarData& otherAvatar = otherNodeData->getAvatar(); - // Decide whether to send this avatar's data based on it's distance from us - - // The full rate distance is the distance at which EVERY update will be sent for this avatar - // at twice the full rate distance, there will be a 50% chance of sending this avatar's update - glm::vec3 otherPosition = otherAvatar.getPosition(); - float distanceToAvatar = glm::length(myPosition - otherPosition); - - // potentially update the max full rate distance for this frame - maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - - if (distanceToAvatar != 0.0f - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { - return; - } - - AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); - - if (lastSeqToReceiver > lastSeqFromSender && lastSeqToReceiver != UINT16_MAX) { - // we got out out of order packets from the sender, track it - otherNodeData->incrementNumOutOfOrderSends(); - } - - // make sure we haven't already sent this data from this sender to this receiver - // or that somehow we haven't sent - if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { - ++numAvatarsHeldBack; - return; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { - // this is a skip - we still send the packet but capture the presence of the skip so we see it happening - ++numAvatarsWithSkippedFrames; - } - - // we're going to send this avatar - - // increment the number of avatars sent to this reciever - nodeData->incrementNumAvatarsSentLastFrame(); - - // set the last sent sequence number for this sender on the receiver - nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), - otherNodeData->getLastReceivedSequenceNumber()); - - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); - - numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += - avatarPacketList->write(otherAvatar.toByteArray(false, distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO)); - - avatarPacketList->endSegment(); - }); - - // close the current packet so that we're always sending something - avatarPacketList->closeCurrentPacket(true); - - // send the avatar data PacketList - nodeList->sendPacketList(std::move(avatarPacketList), *node); - - // record the bytes sent for other avatar data in the MessagesMixerClientData - nodeData->recordSentAvatarData(numAvatarDataBytes); - - // record the number of avatars held back this frame - nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); - nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); - - if (numOtherAvatars == 0) { - // update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send - nodeData->setMaxAvatarDistance(FLT_MAX); - } else { - nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); - } - } - ); - - qDebug() << "MessagesMixer::broadcastMessagesData()... calling nodeList->eachMatchingNode() for encode..."; - - // We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so - // that we can notice differences, next time around. - nodeList->eachMatchingNode( - [&](const SharedNodePointer& otherNode)->bool { - if (!otherNode->getLinkedData()) { - return false; - } - if (otherNode->getType() != NodeType::Agent) { - return false; - } - if (!otherNode->getActiveSocket()) { - return false; - } - return true; - }, - [&](const SharedNodePointer& otherNode) { - MessagesMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - MutexTryLocker lock(otherNodeData->getMutex()); - if (!lock.isLocked()) { - return; - } - AvatarData& otherAvatar = otherNodeData->getAvatar(); - otherAvatar.doneEncoding(false); - }); - - _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); -} - void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { + qDebug() << "MessagesMixer::nodeKilled()... node:" << killedNode->getUUID(); + if (killedNode->getType() == NodeType::Agent && killedNode->getLinkedData()) { auto nodeList = DependencyManager::get(); - // this was an avatar we were sending to other people - // send a kill packet for it to our other nodes - auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); - killPacket->write(killedNode->getUUID().toRfc4122()); - - nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); - // we also want to remove sequence number data for this avatar on our other avatars // so invoke the appropriate method on the MessagesMixerClientData for other avatars nodeList->eachMatchingNode( @@ -414,18 +86,32 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { return true; }, [&](const SharedNodePointer& node) { - QMetaObject::invokeMethod(node->getLinkedData(), - "removeLastBroadcastSequenceNumber", - Qt::AutoConnection, - Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); + qDebug() << "eachMatchingNode()... node:" << node->getUUID(); } ); } } -void MessagesMixer::handleMessagesDataPacket(QSharedPointer packet, SharedNodePointer senderNode) { +void MessagesMixer::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { + qDebug() << "MessagesMixer::handleMessagesPacket()... senderNode:" << senderNode->getUUID(); + auto nodeList = DependencyManager::get(); - nodeList->updateNodeWithDataFromPacket(packet, senderNode); + //nodeList->updateNodeWithDataFromPacket(packet, senderNode); + + QByteArray data = packetList->getMessage(); + auto packetType = packetList->getType(); + + if (packetType == PacketType::MessagesData) { + QString message = QString::fromUtf8(data); + qDebug() << "got a messages packet:" << message; + + // this was an avatar we were sending to other people + // send a kill packet for it to our other nodes + //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); + //killPacket->write(killedNode->getUUID().toRfc4122()); + //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); + + } } void MessagesMixer::sendStatsPacket() { @@ -484,6 +170,7 @@ void MessagesMixer::run() { node->setLinkedData(new MessagesMixerClientData()); }; + /* // setup the timer that will be fired on the broadcast thread _broadcastTimer = new QTimer; _broadcastTimer->setInterval(MESSAGES_DATA_SEND_INTERVAL_MSECS); @@ -492,6 +179,7 @@ void MessagesMixer::run() { // connect appropriate signals and slots connect(_broadcastTimer, &QTimer::timeout, this, &MessagesMixer::broadcastMessagesData, Qt::DirectConnection); connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start())); + */ // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); @@ -516,7 +204,7 @@ void MessagesMixer::run() { parseDomainServerSettings(domainHandler.getSettingsObject()); // start the broadcastThread - _broadcastThread.start(); + //_broadcastThread.start(); } void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index d96a20dd18..0d390b83a4 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -32,7 +32,7 @@ public slots: void sendStatsPacket(); private slots: - void handleMessagesDataPacket(QSharedPointer packet, SharedNodePointer senderNode); + void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); private: void broadcastMessagesData(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96b8ab74a8..692b372ae4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -339,6 +340,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); return true; @@ -484,6 +486,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); assetThread->start(); + // Setup MessagesClient + auto messagesClient = DependencyManager::get(); + QThread* messagesThread = new QThread; + messagesThread->setObjectName("Messages Client Thread"); + messagesClient->moveToThread(messagesThread); + connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); + messagesThread->start(); + const DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); @@ -4019,6 +4029,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("Messages", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index d3d44b6fdc..7517bb6535 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -74,34 +74,25 @@ bool haveMessagesMixer() { return true; } -void MessagesClient::handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode) { - auto packetType = packet->getType(); +void MessagesClient::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { + QByteArray data = packetList->getMessage(); + auto packetType = packetList->getType(); if (packetType == PacketType::MessagesData) { - qDebug() << "got a messages packet"; + QString message = QString::fromUtf8(data); + qDebug() << "got a messages packet:" << message; } } void MessagesClient::sendMessage(const QString& channel, const QString& message) { + qDebug() << "MessagesClient::sendMessage() channel:" << channel << "message:" << message; auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); if (messagesMixer) { auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); - - #if 0 - auto messageID = ++_currentID; - packetList->writePrimitive(messageID); - - packetList->writePrimitive(static_cast(extension.length())); - packetList->write(extension.toLatin1().constData(), extension.length()); - - uint64_t size = data.length(); - packetList->writePrimitive(size); - packetList->write(data.constData(), size); - - nodeList->sendPacketList(std::move(packetList), *assetServer); - #endif + packetList->write(message.toUtf8()); + nodeList->sendPacketList(std::move(packetList), *messagesMixer); } } diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index f1f13bfe20..121e6041b1 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -31,7 +31,7 @@ public: Q_INVOKABLE void sendMessage(const QString& channel, const QString& message); private slots: - void handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode); + void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); void handleNodeKilled(SharedNodePointer node); private: From f40ff69c750b499c3d1f74fd02b6393af122a2ae Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Mon, 16 Nov 2015 17:27:00 -0800 Subject: [PATCH 0946/1003] added scripts for group recording --- .../entityScripts/recordingEntityScript.js | 91 ++++++++++++++ examples/entityScripts/recordingMaster.js | 116 ++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 examples/entityScripts/recordingEntityScript.js create mode 100644 examples/entityScripts/recordingMaster.js diff --git a/examples/entityScripts/recordingEntityScript.js b/examples/entityScripts/recordingEntityScript.js new file mode 100644 index 0000000000..ede6f4fbe2 --- /dev/null +++ b/examples/entityScripts/recordingEntityScript.js @@ -0,0 +1,91 @@ +// +// recordingEntityScript.js +// examples/entityScripts +// +// Created by Alessandro Signa on 11/12/15. +// Copyright 2015 High Fidelity, Inc. +// + +// All the avatars in the area when the master presses the button will start/stop recording. +// + +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + + + +(function() { + var insideRecorderArea = false; + var enteredInTime = false; + var isAvatarRecording = false; + var _this; + + function recordingEntity() { + _this = this; + return; + } + + recordingEntity.prototype = { + update: function(){ + var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); + var isRecordingStarted = userData.recordingKey.isRecordingStarted; + if(isRecordingStarted && !isAvatarRecording){ + _this.startRecording(); + }else if((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)){ + _this.stopRecording(); + }else if(!isRecordingStarted && insideRecorderArea && !enteredInTime){ + //if an avatar enters the zone while a recording is started he will be able to participate to the next group recording + enteredInTime = true; + } + + }, + preload: function(entityID) { + this.entityID = entityID; + Script.update.connect(_this.update); + }, + enterEntity: function(entityID) { + print("entering in the recording area"); + insideRecorderArea = true; + var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); + var isRecordingStarted = userData.recordingKey.isRecordingStarted; + if(!isRecordingStarted){ + //i'm in the recording area in time (before the event starts) + enteredInTime = true; + } + }, + leaveEntity: function(entityID) { + print("leaving the recording area"); + insideRecorderArea = false; + enteredInTime = false; + }, + + startRecording: function(entityID){ + if(enteredInTime && !isAvatarRecording){ + print("RECORDING STARTED"); + Recording.startRecording(); + isAvatarRecording = true; + } + }, + + stopRecording: function(entityID){ + if(isAvatarRecording){ + print("RECORDING ENDED"); + Recording.stopRecording(); + Recording.loadLastRecording(); + isAvatarRecording = false; + recordingFile = Window.save("Save recording to file", "./groupRecording", "Recordings (*.hfr)"); + if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { + Recording.saveRecording(recordingFile); + } + } + }, + clean: function(entityID) { + Script.update.disconnect(_this.update); + } + } + + + + return new recordingEntity(); +}); diff --git a/examples/entityScripts/recordingMaster.js b/examples/entityScripts/recordingMaster.js new file mode 100644 index 0000000000..3cec521ce0 --- /dev/null +++ b/examples/entityScripts/recordingMaster.js @@ -0,0 +1,116 @@ +// +// recordingMaster.js +// examples/entityScripts +// +// Created by Alessandro Signa on 11/12/15. +// Copyright 2015 High Fidelity, Inc. +// +// Run this script to spawn a box (recorder) and drive the start/end of the recording for anyone who is inside the box +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var PARAMS_SCRIPT_URL = Script.resolvePath('recordingEntityScript.js'); + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +Script.include("../libraries/toolBars.js"); +Script.include("../libraries/utils.js"); + + + +var rotation = Quat.safeEulerAngles(Camera.getOrientation()); +rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1, Quat.getFront(rotation))); + +var TOOL_ICON_URL = HIFI_PUBLIC_BUCKET + "images/tools/"; +var ALPHA_ON = 1.0; +var ALPHA_OFF = 0.7; +var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; + +var toolBar = null; +var recordIcon; + + + +var isRecording = false; + +var recordAreaEntity = Entities.addEntity({ + name: 'recorderEntity', + dimensions: { + x: 2, + y: 1, + z: 2 + }, + type: 'Box', + position: center, + color: { + red: 255, + green: 255, + blue: 255 + }, + visible: true, + ignoreForCollisions: true, + script: PARAMS_SCRIPT_URL, + + userData: JSON.stringify({ + recordingKey: { + isRecordingStarted: false + } + }) +}); + + +setupToolBar(); + +function setupToolBar() { + if (toolBar != null) { + print("Multiple calls to setupToolBar()"); + return; + } + Tool.IMAGE_HEIGHT /= 2; + Tool.IMAGE_WIDTH /= 2; + + toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); //put the button in the up-left corner + + toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); + + recordIcon = toolBar.addTool({ + imageURL: TOOL_ICON_URL + "recording-record.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + x: 0, y: 0, + width: Tool.IMAGE_WIDTH, + height: Tool.IMAGE_HEIGHT, + alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, + visible: true + }, true, isRecording); + +} + +function mousePressEvent(event) { + clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (recordIcon === toolBar.clicked(clickedOverlay, false)) { + if (!isRecording) { + print("I'm the master. I want to start recording"); + isRecording = true; + setEntityCustomData("recordingKey", recordAreaEntity, {isRecordingStarted: true}); + + } else { + print("I want to stop recording"); + isRecording = false; + setEntityCustomData("recordingKey", recordAreaEntity, {isRecordingStarted: false}); + + } + } +} + + +function cleanup() { + toolBar.cleanup(); + Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings + Entities.deleteEntity(recordAreaEntity); +} + + + + Script.scriptEnding.connect(cleanup); + Controller.mousePressEvent.connect(mousePressEvent); \ No newline at end of file From 71cc36fdd8771ccd17d874e691a4734518378d2b Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 16 Nov 2015 18:31:24 -0800 Subject: [PATCH 0947/1003] removing ms to seconds conversion in MyAvatar --- interface/src/scripting/RecordingScriptingInterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index bf585f5481..32bd6fde97 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -56,11 +56,11 @@ bool RecordingScriptingInterface::isPaused() { } float RecordingScriptingInterface::playerElapsed() { - return (float)_player->position() / MSECS_PER_SECOND; + return _player->position(); } float RecordingScriptingInterface::playerLength() { - return _player->length() / MSECS_PER_SECOND; + return _player->length(); } void RecordingScriptingInterface::loadRecording(const QString& filename) { @@ -103,7 +103,7 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { } void RecordingScriptingInterface::setPlayerTime(float time) { - _player->seek(time * MSECS_PER_SECOND); + _player->seek(time); } void RecordingScriptingInterface::setPlayFromCurrentLocation(bool playFromCurrentLocation) { From 1e0b66a68ffab52f6240273541a4ea4c64da9fd5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 16 Nov 2015 19:23:39 -0800 Subject: [PATCH 0948/1003] more work --- .../src/messages/MessagesMixer.cpp | 28 +++++++++++++++++-- .../src/messages/MessagesMixer.h | 5 ++-- interface/src/Application.cpp | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 6177850532..11e362ad94 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -92,8 +92,8 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { } } -void MessagesMixer::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { - qDebug() << "MessagesMixer::handleMessagesPacket()... senderNode:" << senderNode->getUUID(); +void MessagesMixer::handleMessagesPacketList(QSharedPointer packetList, SharedNodePointer senderNode) { + qDebug() << "MessagesMixer::handleMessagesPacketList()... senderNode:" << senderNode->getUUID(); auto nodeList = DependencyManager::get(); //nodeList->updateNodeWithDataFromPacket(packet, senderNode); @@ -114,6 +114,30 @@ void MessagesMixer::handleMessagesPacket(QSharedPointer packetList } } +void MessagesMixer::handleMessagesPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + qDebug() << "MessagesMixer::handleMessagesPacket()... senderNode:" << sendingNode->getUUID(); + + /* + auto nodeList = DependencyManager::get(); + //nodeList->updateNodeWithDataFromPacket(packet, senderNode); + + QByteArray data = packetList->getMessage(); + auto packetType = packetList->getType(); + + if (packetType == PacketType::MessagesData) { + QString message = QString::fromUtf8(data); + qDebug() << "got a messages packet:" << message; + + // this was an avatar we were sending to other people + // send a kill packet for it to our other nodes + //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); + //killPacket->write(killedNode->getUUID().toRfc4122()); + //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); + + } + */ +} + void MessagesMixer::sendStatsPacket() { QJsonObject statsObject; statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 0d390b83a4..057796bc54 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -32,8 +32,9 @@ public slots: void sendStatsPacket(); private slots: - void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); - + void handleMessagesPacketList(QSharedPointer packetList, SharedNodePointer senderNode); + void handleMessagesPacket(QSharedPointer packet, SharedNodePointer sendingNode); + private: void broadcastMessagesData(); void parseDomainServerSettings(const QJsonObject& domainSettings); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 692b372ae4..712fb7dc02 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -560,7 +560,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // 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 << NodeType::MessagesMixer); // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); From 8553033d59273b2385687c4afe35d8c01b17ef2f Mon Sep 17 00:00:00 2001 From: samcake Date: Mon, 16 Nov 2015 19:27:28 -0800 Subject: [PATCH 0949/1003] Refactoring the mess for the Agent --- assignment-client/src/Agent.cpp | 2 ++ examples/acScripts/ControlACs.js | 2 +- interface/src/scripting/RecordingScriptingInterface.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 063bf24de8..3cefbbe246 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -28,6 +28,7 @@ #include // TODO: consider moving to scriptengine.h #include "avatars/ScriptableAvatar.h" +#include "RecordingScriptingInterface.h" #include "Agent.h" @@ -45,6 +46,7 @@ Agent::Agent(NLPacket& packet) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index ba066d9750..e3d63cc26e 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -156,7 +156,7 @@ function sendCommand(id, action) { position: controlEntityPosition, dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, visible: false, - lifetime: 10, + lifetime: 10000, userData: JSON.stringify({ idKey: { uD_id: id diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 32bd6fde97..1cb29feae4 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -8,6 +8,8 @@ #include "RecordingScriptingInterface.h" +#include + #include #include #include @@ -16,8 +18,8 @@ #include #include -#include "avatar/AvatarManager.h" -#include "Application.h" +//#include "avatar/AvatarManager.h" +//#include "Application.h" #include "InterfaceLogging.h" typedef int16_t AudioSample; From 5f04e4a16715e49275cc155ad7b1e45fd925f965 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 16 Nov 2015 19:29:54 -0800 Subject: [PATCH 0950/1003] more work --- assignment-client/src/messages/MessagesMixer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 11e362ad94..988e2bfeac 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -49,6 +49,7 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket"); + packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacketList"); } MessagesMixer::~MessagesMixer() { From eb4bb1cc03ceb3a5dbdede1e83eb54dc8d3cc297 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 16 Nov 2015 19:34:53 -0800 Subject: [PATCH 0951/1003] more work --- assignment-client/src/messages/MessagesMixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 988e2bfeac..5b1637e337 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -49,7 +49,7 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket"); - packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacketList"); + packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacketList"); } MessagesMixer::~MessagesMixer() { From f712fae4d26df69c9d7e82c3e1824f20d49d01c4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 17 Nov 2015 08:56:21 -0800 Subject: [PATCH 0952/1003] more hacking --- assignment-client/src/messages/MessagesMixer.cpp | 4 ++-- libraries/networking/src/PacketReceiver.cpp | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 988e2bfeac..d9cc772eb2 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -48,8 +48,8 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket"); - packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacketList"); + //packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket"); + packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacketList"); } MessagesMixer::~MessagesMixer() { diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 9d25724f6c..81d8c5ee73 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -95,6 +95,8 @@ void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, } bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, const char* slot) { + qCDebug(networking) << "PacketReceiver::registerMessageListener() packet list type" << type; + Q_ASSERT_X(listener, "PacketReceiver::registerMessageListener", "No object to register"); Q_ASSERT_X(slot, "PacketReceiver::registerMessageListener", "No slot to register"); @@ -110,8 +112,12 @@ bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, // add the mapping _packetListListenerMap[type] = ObjectMethodPair(QPointer(listener), matchingMethod); + + qCDebug(networking) << "Registering a packet listener for packet list type" << type; + return true; } else { + qCDebug(networking) << "NOT Registering a packet listener for packet list type" << type; return false; } } @@ -352,7 +358,7 @@ void PacketReceiver::handleVerifiedPacketList(std::unique_ptr p } } else if (it == _packetListListenerMap.end()) { - qCWarning(networking) << "No listener found for packet type" << nlPacketList->getType(); + qCWarning(networking) << "No listener found for packet list type" << nlPacketList->getType(); // insert a dummy listener so we don't print this again _packetListListenerMap.insert(nlPacketList->getType(), { nullptr, QMetaMethod() }); From 5b92929833a4a16a714ee686923f6147a1901cc1 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 17 Nov 2015 09:32:22 -0800 Subject: [PATCH 0953/1003] Moving the REcordingScriptingInterface into the script-engine and wiiring up in the assignment-client --- assignment-client/src/Agent.cpp | 8 ++++- interface/src/Application.cpp | 7 ++-- .../src}/RecordingScriptingInterface.cpp | 36 ++++++++++++------- .../src}/RecordingScriptingInterface.h | 3 ++ libraries/script-engine/src/ScriptEngine.cpp | 4 +++ 5 files changed, 42 insertions(+), 16 deletions(-) rename {interface/src/scripting => libraries/script-engine/src}/RecordingScriptingInterface.cpp (85%) rename {interface/src/scripting => libraries/script-engine/src}/RecordingScriptingInterface.h (96%) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 3cefbbe246..2c4d190f73 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -24,6 +24,9 @@ #include #include +#include +#include + #include #include // TODO: consider moving to scriptengine.h @@ -46,6 +49,8 @@ Agent::Agent(NLPacket& packet) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); @@ -168,7 +173,7 @@ void Agent::run() { // give this AvatarData object to the script engine setAvatarData(&scriptedAvatar, "Avatar"); - + auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); @@ -245,6 +250,7 @@ void Agent::setIsAvatar(bool isAvatar) { void Agent::setAvatarData(AvatarData* avatarData, const QString& objectName) { _avatarData = avatarData; _scriptEngine->registerGlobalObject(objectName, avatarData); + DependencyManager::get()->setControlledAvatar(avatarData); } void Agent::sendAvatarIdentityPacket() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96b8ab74a8..ee25d6a06a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -126,7 +127,6 @@ #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" -#include "scripting/RecordingScriptingInterface.h" #include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" @@ -733,6 +733,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); + // Assign MyAvatar to th eRecording Singleton + DependencyManager::get()->setControlledAvatar(getMyAvatar()); + + // Now that menu is initalized we can sync myAvatar with it's state. getMyAvatar()->updateMotionBehaviorFromMenu(); @@ -3979,7 +3983,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri RayToOverlayIntersectionResultFromScriptValue); scriptEngine->registerGlobalObject("Desktop", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Recording", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Window", DependencyManager::get().data()); scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp similarity index 85% rename from interface/src/scripting/RecordingScriptingInterface.cpp rename to libraries/script-engine/src/RecordingScriptingInterface.cpp index 1cb29feae4..0973f6870f 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -15,12 +15,12 @@ #include #include #include -#include +//#include #include - +#include //#include "avatar/AvatarManager.h" //#include "Application.h" -#include "InterfaceLogging.h" +#include "ScriptEngineLogging.h" typedef int16_t AudioSample; @@ -45,8 +45,12 @@ RecordingScriptingInterface::RecordingScriptingInterface() { _player = DependencyManager::get(); _recorder = DependencyManager::get(); - auto audioClient = DependencyManager::get(); - connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); +// auto audioClient = DependencyManager::get(); + // connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); +} + +void RecordingScriptingInterface::setControlledAvatar(AvatarData* avatar) { + _controlledAvatar = avatar; } bool RecordingScriptingInterface::isPlaying() { @@ -86,7 +90,8 @@ void RecordingScriptingInterface::startPlaying() { QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection); return; } - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; // Playback from the current position if (_playFromCurrentLocation) { _dummyAvatar.setRecordingBasis(std::make_shared(myAvatar->getTransform())); @@ -154,7 +159,7 @@ float RecordingScriptingInterface::recorderElapsed() { void RecordingScriptingInterface::startRecording() { if (_recorder->isRecording()) { - qCWarning(interfaceapp) << "Recorder is already running"; + qCWarning(scriptengine) << "Recorder is already running"; return; } @@ -165,7 +170,9 @@ void RecordingScriptingInterface::startRecording() { _recordingEpoch = Frame::epochForFrameTime(0); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; myAvatar->setRecordingBasis(); _recorder->start(); } @@ -182,7 +189,9 @@ void RecordingScriptingInterface::stopRecording() { } _lastClip->seek(0); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; myAvatar->clearRecordingBasis(); } @@ -208,7 +217,7 @@ void RecordingScriptingInterface::loadLastRecording() { } if (!_lastClip) { - qCDebug(interfaceapp) << "There is no recording to load"; + qCDebug(scriptengine) << "There is no recording to load"; return; } @@ -221,7 +230,8 @@ void RecordingScriptingInterface::processAvatarFrame(const Frame::ConstPointer& AvatarData::fromFrame(frame->data, _dummyAvatar); - auto myAvatar = DependencyManager::get()->getMyAvatar(); + //auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto myAvatar = _controlledAvatar; if (_useHeadModel && _dummyAvatar.getFaceModelURL().isValid() && (_dummyAvatar.getFaceModelURL() != myAvatar->getFaceModelURL())) { // FIXME @@ -255,6 +265,6 @@ void RecordingScriptingInterface::processAudioInput(const QByteArray& audio) { } void RecordingScriptingInterface::processAudioFrame(const recording::FrameConstPointer& frame) { - auto audioClient = DependencyManager::get(); - audioClient->handleRecordedAudioInput(frame->data); + // auto audioClient = DependencyManager::get(); + // audioClient->handleRecordedAudioInput(frame->data); } diff --git a/interface/src/scripting/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h similarity index 96% rename from interface/src/scripting/RecordingScriptingInterface.h rename to libraries/script-engine/src/RecordingScriptingInterface.h index 510a4b6898..db3a6b1240 100644 --- a/interface/src/scripting/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -24,6 +24,8 @@ class RecordingScriptingInterface : public QObject, public Dependency { public: RecordingScriptingInterface(); + void setControlledAvatar(AvatarData* avatar); + public slots: bool isPlaying(); bool isPaused(); @@ -74,6 +76,7 @@ private: Flag _useSkeletonModel { false }; recording::ClipPointer _lastClip; AvatarData _dummyAvatar; + AvatarData* _controlledAvatar; }; #endif // hifi_RecordingScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f62bf8cd5..f30a5d4234 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -46,6 +46,7 @@ #include "WebSocketClass.h" #include "SceneScriptingInterface.h" +#include "RecordingScriptingInterface.h" #include "MIDIEvent.h" @@ -375,6 +376,9 @@ void ScriptEngine::init() { auto scriptingInterface = DependencyManager::get(); registerGlobalObject("Controller", scriptingInterface.data()); UserInputMapper::registerControllerTypes(this); + + auto recordingInterface = DependencyManager::get(); + registerGlobalObject("Recording", recordingInterface.data()); } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From 6b61ec569ca5203e5a3ede40ca12c56dd98f2fb4 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 09:47:50 -0800 Subject: [PATCH 0954/1003] more work on channels --- .../src/messages/MessagesMixer.cpp | 135 +++++++----------- .../src/messages/MessagesMixer.h | 21 ++- .../src/messages/MessagesMixerClientData.cpp | 55 ------- .../src/messages/MessagesMixerClientData.h | 105 -------------- libraries/networking/src/MessagesClient.cpp | 12 +- libraries/networking/src/udt/PacketHeaders.h | 4 +- 6 files changed, 73 insertions(+), 259 deletions(-) delete mode 100644 assignment-client/src/messages/MessagesMixerClientData.cpp delete mode 100644 assignment-client/src/messages/MessagesMixerClientData.h diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index d9cc772eb2..16b24088e2 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,6 @@ #include #include -#include "MessagesMixerClientData.h" #include "MessagesMixer.h" const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; @@ -35,7 +35,6 @@ const unsigned int MESSAGES_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) MESSAGES_ MessagesMixer::MessagesMixer(NLPacket& packet) : ThreadedAssignment(packet), - _broadcastThread(), _lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()), _trailingSleepRatio(1.0f), _performanceThrottlingRatio(0.0f), @@ -48,17 +47,12 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - //packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagesPacket"); - packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacketList"); + packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessages"); + packetReceiver.registerMessageListener(PacketType::MessagesSubscribe, this, "handleMessagesSubscribe"); + packetReceiver.registerMessageListener(PacketType::MessagesUnsubscribe, this, "handleMessagesUnsubscribe"); } MessagesMixer::~MessagesMixer() { - if (_broadcastTimer) { - _broadcastTimer->deleteLater(); - } - - _broadcastThread.quit(); - _broadcastThread.wait(); } // An 80% chance of sending a identity packet within a 5 second interval. @@ -93,52 +87,55 @@ void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { } } -void MessagesMixer::handleMessagesPacketList(QSharedPointer packetList, SharedNodePointer senderNode) { - qDebug() << "MessagesMixer::handleMessagesPacketList()... senderNode:" << senderNode->getUUID(); +void MessagesMixer::handleMessages(QSharedPointer packetList, SharedNodePointer senderNode) { + Q_ASSERT(packetList->getType() == PacketType::MessagesData); + qDebug() << "MessagesMixer::handleMessages()... senderNode:" << senderNode->getUUID(); + + QByteArray packetData = packetList->getMessage(); + QBuffer packet{ &packetData }; + packet.open(QIODevice::ReadOnly); + + quint16 channelLength; + packet.read(reinterpret_cast(&channelLength), sizeof(channelLength)); + auto channelData = packet.read(channelLength); + QString channel = QString::fromUtf8(channelData); + + quint16 messageLength; + packet.read(reinterpret_cast(&messageLength), sizeof(messageLength)); + auto messageData = packet.read(messageLength); + QString message = QString::fromUtf8(messageData); + + auto nodeList = DependencyManager::get(); - //nodeList->updateNodeWithDataFromPacket(packet, senderNode); - QByteArray data = packetList->getMessage(); - auto packetType = packetList->getType(); + qDebug() << "got a messages:" << message << "on channel:" << channel << "from node:" << senderNode->getUUID(); - if (packetType == PacketType::MessagesData) { - QString message = QString::fromUtf8(data); - qDebug() << "got a messages packet:" << message; + // this was an avatar we were sending to other people + // send a kill packet for it to our other nodes + //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); + //killPacket->write(killedNode->getUUID().toRfc4122()); + //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); +} - // this was an avatar we were sending to other people - // send a kill packet for it to our other nodes - //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); - //killPacket->write(killedNode->getUUID().toRfc4122()); - //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); +void MessagesMixer::handleMessagesSubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { + Q_ASSERT(packetList->getType() == PacketType::MessagesSubscribe); + QString channel = QString::fromUtf8(packetList->getMessage()); + qDebug() << "Node [" << senderNode->getUUID() << "] subscribed to channel:" << channel; + _channelSubscribers[channel] << senderNode->getUUID(); +} +void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { + Q_ASSERT(packetList->getType() == PacketType::MessagesUnsubscribe); + QString channel = QString::fromUtf8(packetList->getMessage()); + qDebug() << "Node [" << senderNode->getUUID() << "] unsubscribed from channel:" << channel; + + if (_channelSubscribers.contains(channel)) { + _channelSubscribers[channel].remove(senderNode->getUUID()); } } -void MessagesMixer::handleMessagesPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - qDebug() << "MessagesMixer::handleMessagesPacket()... senderNode:" << sendingNode->getUUID(); - - /* - auto nodeList = DependencyManager::get(); - //nodeList->updateNodeWithDataFromPacket(packet, senderNode); - - QByteArray data = packetList->getMessage(); - auto packetType = packetList->getType(); - - if (packetType == PacketType::MessagesData) { - QString message = QString::fromUtf8(data); - qDebug() << "got a messages packet:" << message; - - // this was an avatar we were sending to other people - // send a kill packet for it to our other nodes - //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); - //killPacket->write(killedNode->getUUID().toRfc4122()); - //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); - - } - */ -} - +// FIXME - make these stats relevant void MessagesMixer::sendStatsPacket() { QJsonObject statsObject; statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; @@ -160,18 +157,6 @@ void MessagesMixer::sendStatsPacket() { messagesStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); messagesStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth(); - MessagesMixerClientData* clientData = static_cast(node->getLinkedData()); - if (clientData) { - MutexTryLocker lock(clientData->getMutex()); - if (lock.isLocked()) { - clientData->loadJSONStats(messagesStats); - - // add the diff between the full outbound bandwidth and the measured bandwidth for AvatarData send only - messagesStats["delta_full_vs_avatar_data_kbps"] = - messagesStats[NODE_OUTBOUND_KBPS_STAT_KEY].toDouble() - messagesStats[OUTBOUND_MESSAGES_DATA_STATS_KEY].toDouble(); - } - } - messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats; }); @@ -192,27 +177,15 @@ void MessagesMixer::run() { nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->linkedDataCreateCallback = [] (Node* node) { - node->setLinkedData(new MessagesMixerClientData()); + // no need to link data }; - /* - // setup the timer that will be fired on the broadcast thread - _broadcastTimer = new QTimer; - _broadcastTimer->setInterval(MESSAGES_DATA_SEND_INTERVAL_MSECS); - _broadcastTimer->moveToThread(&_broadcastThread); - - // connect appropriate signals and slots - connect(_broadcastTimer, &QTimer::timeout, this, &MessagesMixer::broadcastMessagesData, Qt::DirectConnection); - connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start())); - */ - // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); qDebug() << "Waiting for domain settings from domain-server."; // block until we get the settingsRequestComplete signal - QEventLoop loop; connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); @@ -227,22 +200,16 @@ void MessagesMixer::run() { // parse the settings to pull out the values we need parseDomainServerSettings(domainHandler.getSettingsObject()); - - // start the broadcastThread - //_broadcastThread.start(); } void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { qDebug() << "MessagesMixer::parseDomainServerSettings() domainSettings:" << domainSettings; const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; - const QString NODE_SEND_BANDWIDTH_KEY = "max_node_send_bandwidth"; - const float DEFAULT_NODE_SEND_BANDWIDTH = 1.0f; - QJsonValue nodeBandwidthValue = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY]; - if (!nodeBandwidthValue.isDouble()) { - qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value"; - } - - _maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA; - qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps."; + // TODO - if we want options, parse them here... + // + // QJsonValue nodeBandwidthValue = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY]; + // if (!nodeBandwidthValue.isDouble()) { + // qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value"; + // } } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 057796bc54..cd15ea7d2a 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -23,23 +23,22 @@ class MessagesMixer : public ThreadedAssignment { public: MessagesMixer(NLPacket& packet); ~MessagesMixer(); -public slots: - /// runs the avatar mixer - void run(); +public slots: + void run(); void nodeKilled(SharedNodePointer killedNode); - void sendStatsPacket(); private slots: - void handleMessagesPacketList(QSharedPointer packetList, SharedNodePointer senderNode); - void handleMessagesPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleMessages(QSharedPointer packetList, SharedNodePointer senderNode); + void handleMessagesSubscribe(QSharedPointer packetList, SharedNodePointer senderNode); + void handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode); private: - void broadcastMessagesData(); void parseDomainServerSettings(const QJsonObject& domainSettings); - - QThread _broadcastThread; + + + QHash> _channelSubscribers; quint64 _lastFrameTimestamp; @@ -50,10 +49,6 @@ private: int _numStatFrames; int _sumBillboardPackets; int _sumIdentityPackets; - - float _maxKbpsPerNode = 0.0f; - - QTimer* _broadcastTimer = nullptr; }; #endif // hifi_MessagesMixer_h diff --git a/assignment-client/src/messages/MessagesMixerClientData.cpp b/assignment-client/src/messages/MessagesMixerClientData.cpp deleted file mode 100644 index 6aa8f39c22..0000000000 --- a/assignment-client/src/messages/MessagesMixerClientData.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// MessagesMixerClientData.cpp -// assignment-client/src/messages -// -// Created by Brad hefta-Gaub on 11/16/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 -// - -#include - -#include "MessagesMixerClientData.h" - -int MessagesMixerClientData::parseData(NLPacket& packet) { - // pull the sequence number from the data first - packet.readPrimitive(&_lastReceivedSequenceNumber); - - // compute the offset to the data payload - return _avatar.parseDataFromBuffer(packet.readWithoutCopy(packet.bytesLeftToRead())); -} - -bool MessagesMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid) { - if (_hasReceivedFirstPacketsFrom.find(uuid) == _hasReceivedFirstPacketsFrom.end()) { - _hasReceivedFirstPacketsFrom.insert(uuid); - return false; - } - return true; -} - -uint16_t MessagesMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { - // return the matching PacketSequenceNumber, or the default if we don't have it - auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); - if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { - return nodeMatch->second; - } else { - return 0; - } -} - -void MessagesMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { - jsonObject["display_name"] = _avatar.getDisplayName(); - jsonObject["full_rate_distance"] = _fullRateDistance; - jsonObject["max_av_distance"] = _maxAvatarDistance; - jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame; - jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); - jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); - jsonObject["total_num_out_of_order_sends"] = _numOutOfOrderSends; - - jsonObject[OUTBOUND_MESSAGES_DATA_STATS_KEY] = getOutboundAvatarDataKbps(); - jsonObject[INBOUND_MESSAGES_DATA_STATS_KEY] = _avatar.getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT; - - jsonObject["av_data_receive_rate"] = _avatar.getReceiveRate(); -} diff --git a/assignment-client/src/messages/MessagesMixerClientData.h b/assignment-client/src/messages/MessagesMixerClientData.h deleted file mode 100644 index 1667df431f..0000000000 --- a/assignment-client/src/messages/MessagesMixerClientData.h +++ /dev/null @@ -1,105 +0,0 @@ -// -// MessagesMixerClientData.h -// assignment-client/src/messages -// -// Created by Brad hefta-Gaub on 11/16/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 -// - -#ifndef hifi_MessagesMixerClientData_h -#define hifi_MessagesMixerClientData_h - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -const QString OUTBOUND_MESSAGES_DATA_STATS_KEY = "outbound_av_data_kbps"; -const QString INBOUND_MESSAGES_DATA_STATS_KEY = "inbound_av_data_kbps"; - -class MessagesMixerClientData : public NodeData { - Q_OBJECT -public: - int parseData(NLPacket& packet); - AvatarData& getAvatar() { return _avatar; } - - bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid); - - uint16_t getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const; - void setLastBroadcastSequenceNumber(const QUuid& nodeUUID, uint16_t sequenceNumber) - { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } - Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } - - uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } - - quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; } - void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; } - - quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } - void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; } - - void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } - float getFullRateDistance() const { return _fullRateDistance; } - - void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; } - float getMaxAvatarDistance() const { return _maxAvatarDistance; } - - void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } - void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } - int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; } - - void recordNumOtherAvatarStarves(int numAvatarsHeldBack) { _otherAvatarStarves.updateAverage((float) numAvatarsHeldBack); } - float getAvgNumOtherAvatarStarvesPerSecond() const { return _otherAvatarStarves.getAverageSampleValuePerSecond(); } - - void recordNumOtherAvatarSkips(int numOtherAvatarSkips) { _otherAvatarSkips.updateAverage((float) numOtherAvatarSkips); } - float getAvgNumOtherAvatarSkipsPerSecond() const { return _otherAvatarSkips.getAverageSampleValuePerSecond(); } - - void incrementNumOutOfOrderSends() { ++_numOutOfOrderSends; } - - int getNumFramesSinceFRDAdjustment() const { return _numFramesSinceAdjustment; } - void incrementNumFramesSinceFRDAdjustment() { ++_numFramesSinceAdjustment; } - void resetNumFramesSinceFRDAdjustment() { _numFramesSinceAdjustment = 0; } - - void recordSentAvatarData(int numBytes) { _avgOtherAvatarDataRate.updateAverage((float) numBytes); } - - float getOutboundAvatarDataKbps() const - { return _avgOtherAvatarDataRate.getAverageSampleValuePerSecond() / (float) BYTES_PER_KILOBIT; } - - void loadJSONStats(QJsonObject& jsonObject) const; -private: - AvatarData _avatar; - - uint16_t _lastReceivedSequenceNumber { 0 }; - std::unordered_map _lastBroadcastSequenceNumbers; - std::unordered_set _hasReceivedFirstPacketsFrom; - - quint64 _billboardChangeTimestamp = 0; - quint64 _identityChangeTimestamp = 0; - - float _fullRateDistance = FLT_MAX; - float _maxAvatarDistance = FLT_MAX; - - int _numAvatarsSentLastFrame = 0; - int _numFramesSinceAdjustment = 0; - - SimpleMovingAverage _otherAvatarStarves; - SimpleMovingAverage _otherAvatarSkips; - int _numOutOfOrderSends = 0; - - SimpleMovingAverage _avgOtherAvatarDataRate; -}; - -#endif // hifi_MessagesMixerClientData_h diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 7517bb6535..cee7206c31 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -91,7 +91,17 @@ void MessagesClient::sendMessage(const QString& channel, const QString& message) if (messagesMixer) { auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); - packetList->write(message.toUtf8()); + + auto channelUtf8 = channel.toUtf8(); + quint16 channelLength = channelUtf8.length(); + packetList->writePrimitive(channelLength); + packetList->write(channelUtf8); + + auto messageUtf8 = message.toUtf8(); + quint16 messageLength = messageUtf8.length(); + packetList->writePrimitive(messageLength); + packetList->write(messageUtf8); + nodeList->sendPacketList(std::move(packetList), *messagesMixer); } } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d1287d4a08..e0a847dcc6 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -88,7 +88,9 @@ public: AssetGetInfoReply, DomainDisconnectRequest, DomainServerRemovedNode, - MessagesData + MessagesData, + MessagesSubscribe, + MessagesUnsubscribe }; }; From b062d23f612e780abee293bf5483d53a2b5ade33 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 09:51:52 -0800 Subject: [PATCH 0955/1003] bumper is now an equip toggle --- examples/controllers/handControllerGrab.js | 103 ++++++++++++++++++--- examples/libraries/utils.js | 10 +- libraries/entities/src/EntityTree.cpp | 8 ++ 3 files changed, 105 insertions(+), 16 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index d308b3dc49..c50a1418bd 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -28,6 +28,8 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value var TRIGGER_ON_VALUE = 0.4; var TRIGGER_OFF_VALUE = 0.15; +var BUMPER_ON_VALUE = 0.5; + // // distant manipulation // @@ -106,6 +108,11 @@ var STATE_CONTINUE_NEAR_TRIGGER = 7; var STATE_FAR_TRIGGER = 8; var STATE_CONTINUE_FAR_TRIGGER = 9; var STATE_RELEASE = 10; +var STATE_EQUIP_SEARCHING = 11; +var STATE_EQUIP = 12 +var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down +var STATE_CONTINUE_EQUIP = 14; +var STATE_WAITING_FOR_BUMPER_RELEASE = 15; function stateToName(state) { @@ -132,6 +139,16 @@ function stateToName(state) { return "continue_far_trigger"; case STATE_RELEASE: return "release"; + case STATE_EQUIP_SEARCHING: + return "equip_searching"; + case STATE_EQUIP: + return "equip"; + case STATE_CONTINUE_EQUIP_BD: + return "continue_equip_bd"; + case STATE_CONTINUE_EQUIP: + return "continue_equip"; + case STATE_WAITING_FOR_BUMPER_RELEASE: + return "waiting_for_bumper_release"; } return "unknown"; @@ -182,6 +199,7 @@ function MyController(hand) { this.pointer = null; // entity-id of line object this.triggerValue = 0; // rolling average of trigger value this.rawTriggerValue = 0; + this.rawBumperValue = 0; this.offsetPosition = { x: 0.0, y: 0.0, z: 0.0 }; this.offsetRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; @@ -200,6 +218,9 @@ function MyController(hand) { case STATE_SEARCHING: this.search(); break; + case STATE_EQUIP_SEARCHING: + this.search(); + break; case STATE_DISTANCE_HOLDING: this.distanceHolding(); break; @@ -209,7 +230,15 @@ function MyController(hand) { case STATE_NEAR_GRABBING: this.nearGrabbing(); break; + case STATE_EQUIP: + this.nearGrabbing(); + break; + case STATE_WAITING_FOR_BUMPER_RELEASE: + this.waitingForBumperRelease(); + break; case STATE_CONTINUE_NEAR_GRABBING: + case STATE_CONTINUE_EQUIP_BD: + case STATE_CONTINUE_EQUIP: this.continueNearGrabbing(); break; case STATE_NEAR_TRIGGER: @@ -281,10 +310,15 @@ function MyController(hand) { this.pointer = null; }; - this.eitherTrigger = function (value) { + this.triggerPress = function (value) { _this.rawTriggerValue = value; }; + this.bumperPress = function (value) { + _this.rawBumperValue = value; + }; + + this.updateSmoothedTrigger = function () { var triggerValue = this.rawTriggerValue; // smooth out trigger value @@ -305,23 +339,37 @@ function MyController(hand) { return triggerValue > TRIGGER_ON_VALUE; }; + this.bumperSqueezed = function() { + return _this.rawBumperValue > BUMPER_ON_VALUE; + } + + this.bumperReleased = function() { + return _this.rawBumperValue < BUMPER_ON_VALUE; + } + + this.off = function() { if (this.triggerSmoothedSqueezed()) { this.lastPickTime = 0; this.setState(STATE_SEARCHING); return; } + if (this.bumperSqueezed()) { + this.lastPickTime = 0; + this.setState(STATE_EQUIP_SEARCHING); + return; + } } this.search = function() { this.grabbedEntity = null; - //if this hand is the one that's disabled, we don't want to search for anything at all + // if this hand is the one that's disabled, we don't want to search for anything at all if (this.hand === disabledHand) { return; } - if (this.triggerSmoothedReleased()) { + if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) { this.setState(STATE_RELEASE); return; } @@ -398,7 +446,7 @@ function MyController(hand) { return; } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; - this.setState(STATE_NEAR_GRABBING); + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) return; } } else if (! entityIsGrabbedByOther(intersection.entityID)) { @@ -407,8 +455,14 @@ function MyController(hand) { && !intersection.properties.locked) { // the hand is far from the intersected object. go into distance-holding mode this.grabbedEntity = intersection.entityID; - this.setState(STATE_DISTANCE_HOLDING); - return; + if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { + // if a distance pick in equip mode hits something with a spatialKey, equip it + this.setState(STATE_EQUIP); + return; + } else if (this.state == STATE_SEARCHING) { + this.setState(STATE_DISTANCE_HOLDING); + return; + } } else if (grabbableData.wantsTrigger) { this.grabbedEntity = intersection.entityID; this.setState(STATE_FAR_TRIGGER); @@ -434,6 +488,7 @@ function MyController(hand) { var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); var minDistance = PICK_MAX_DISTANCE; var i, props, distance, grabbableData; + this.grabbedEntity = null; for (i = 0; i < nearbyEntities.length; i++) { var grabbableDataForCandidate = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); @@ -490,7 +545,7 @@ function MyController(hand) { this.setState(STATE_NEAR_TRIGGER); return; } else if (!props.locked && props.collisionsWillMove) { - this.setState(STATE_NEAR_GRABBING); + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) return; } }; @@ -634,13 +689,12 @@ function MyController(hand) { return; } - if (this.triggerSmoothedReleased()) { + if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } - this.lineOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -686,7 +740,7 @@ function MyController(hand) { this.actionID = null; } else { this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - this.setState(STATE_CONTINUE_NEAR_GRABBING); + this.setState(this.state == STATE_NEAR_GRABBING ? STATE_CONTINUE_NEAR_GRABBING : STATE_CONTINUE_EQUIP_BD) if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { @@ -696,17 +750,26 @@ function MyController(hand) { } - this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;; + this.currentHandControllerTipPosition = + (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition; this.currentObjectTime = Date.now(); }; this.continueNearGrabbing = function() { - if (this.triggerSmoothedReleased()) { + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); return; } + if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) { + this.setState(STATE_CONTINUE_EQUIP); + return; + } + if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) { + this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); + return; + } // Keep track of the fingertip velocity to impart when we release the object. // Note that the idea of using a constant 'tip' velocity regardless of the @@ -740,6 +803,13 @@ function MyController(hand) { } }; + this.waitingForBumperRelease = function() { + if (this.bumperReleased()) { + this.setState(STATE_RELEASE); + Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); + } + } + this.nearTrigger = function() { if (this.triggerSmoothedReleased()) { this.setState(STATE_RELEASE); @@ -919,6 +989,7 @@ function MyController(hand) { } Entities.editEntity(entityID, whileHeldProperties); } + setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data); return data; }; @@ -948,8 +1019,12 @@ var leftController = new MyController(LEFT_HAND); var MAPPING_NAME = "com.highfidelity.handControllerGrab"; var mapping = Controller.newMapping(MAPPING_NAME); -mapping.from([Controller.Standard.RB, Controller.Standard.RT]).peek().to(rightController.eitherTrigger); -mapping.from([Controller.Standard.LB, Controller.Standard.LT]).peek().to(leftController.eitherTrigger); +mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress); +mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress); + +mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress); +mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress); + Controller.enableMapping(MAPPING_NAME); diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 25900471c1..5d14bfb7dd 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -11,6 +11,12 @@ vec3toStr = function(v, digits) { return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }"; } +quatToStr = function(q, digits) { + if (!digits) { digits = 3; } + return "{ " + q.w.toFixed(digits) + ", " + q.x.toFixed(digits) + ", " + + q.y.toFixed(digits) + ", " + q.z.toFixed(digits)+ " }"; +} + vec3equal = function(v0, v1) { return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z); } @@ -51,7 +57,7 @@ addLine = function(origin, vector, color) { // FIXME fetch from a subkey of user data to support non-destructive modifications setEntityUserData = function(id, data) { var json = JSON.stringify(data) - Entities.editEntity(id, { userData: json }); + Entities.editEntity(id, { userData: json }); } // FIXME do non-destructive modification of the existing user data @@ -60,7 +66,7 @@ getEntityUserData = function(id) { var properties = Entities.getEntityProperties(id, "userData"); if (properties.userData) { try { - results = JSON.parse(properties.userData); + results = JSON.parse(properties.userData); } catch(err) { logDebug(err); logDebug(properties.userData); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 027972549a..dc7c19056a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -704,6 +704,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList= 0) { + QString changeHint = properties.getUserData(); + changedProperties[index] = QString("userData:") + changeHint; + } + } } int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength, From f9a674bca5ab5cde3eafe40a7d363ab7ad73756d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 10:01:31 -0800 Subject: [PATCH 0956/1003] implement subscribe/unsubscribe in MessagesClient --- libraries/networking/src/MessagesClient.cpp | 27 +++++++++++++++++++++ libraries/networking/src/MessagesClient.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index cee7206c31..175c31beb8 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -106,6 +106,33 @@ void MessagesClient::sendMessage(const QString& channel, const QString& message) } } +// FIXME - we should keep track of the channels we are subscribed to locally, and +// in the event that they mixer goes away and/or comes back we should automatically +// resubscribe to those channels +void MessagesClient::subscribe(const QString& channel) { + qDebug() << "MessagesClient::subscribe() channel:" << channel; + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (messagesMixer) { + auto packetList = NLPacketList::create(PacketType::MessagesSubscribe, QByteArray(), true, true); + packetList->write(channel.toUtf8()); + nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } +} + +void MessagesClient::unsubscribe(const QString& channel) { + qDebug() << "MessagesClient::unsubscribe() channel:" << channel; + auto nodeList = DependencyManager::get(); + SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (messagesMixer) { + auto packetList = NLPacketList::create(PacketType::MessagesUnsubscribe, QByteArray(), true, true); + packetList->write(channel.toUtf8()); + nodeList->sendPacketList(std::move(packetList), *messagesMixer); + } +} + void MessagesClient::handleNodeKilled(SharedNodePointer node) { if (node->getType() != NodeType::MessagesMixer) { return; diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index 121e6041b1..2c699850f6 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -29,6 +29,8 @@ public: Q_INVOKABLE void init(); Q_INVOKABLE void sendMessage(const QString& channel, const QString& message); + Q_INVOKABLE void subscribe(const QString& channel); + Q_INVOKABLE void unsubscribe(const QString& channel); private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); From 1f7c70a0d1ef141512a9db231585bc650b468bc1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 10:28:50 -0800 Subject: [PATCH 0957/1003] implement proper sending --- .../src/messages/MessagesMixer.cpp | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 16b24088e2..8f69b86367 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -116,6 +116,31 @@ void MessagesMixer::handleMessages(QSharedPointer packetList, Shar //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); //killPacket->write(killedNode->getUUID().toRfc4122()); //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); + + nodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + + return node->getType() == NodeType::Agent && node->getActiveSocket() && + _channelSubscribers[channel].contains(node->getUUID()); + }, + [&](const SharedNodePointer& node) { + + qDebug() << "sending a messages:" << message << "on channel:" << channel << "to node:" << node->getUUID(); + + auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); + + auto channelUtf8 = channel.toUtf8(); + quint16 channelLength = channelUtf8.length(); + packetList->writePrimitive(channelLength); + packetList->write(channelUtf8); + + auto messageUtf8 = message.toUtf8(); + quint16 messageLength = messageUtf8.length(); + packetList->writePrimitive(messageLength); + packetList->write(messageUtf8); + + nodeList->sendPacketList(std::move(packetList), *node); + }); } void MessagesMixer::handleMessagesSubscribe(QSharedPointer packetList, SharedNodePointer senderNode) { From c27a127739a34e9203a56ef6d25b86aeb7484dad Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 10:41:26 -0800 Subject: [PATCH 0958/1003] add example script --- examples/example/messagesExample.js | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 examples/example/messagesExample.js diff --git a/examples/example/messagesExample.js b/examples/example/messagesExample.js new file mode 100644 index 0000000000..39ee4a3dbe --- /dev/null +++ b/examples/example/messagesExample.js @@ -0,0 +1,38 @@ +var totalTime = 0; +var unsubscribedForTime = 0; +var subscribedForTime = 0; +var subscribed = false; +var SWITCH_SUBSCRIPTION_TIME = 10; +Script.update.connect(function (deltaTime) { + var channel = "example"; + totalTime += deltaTime; + if (!subscribed) { + unsubscribedForTime += deltaTime; + } else { + subscribedForTime += deltaTime; + } + + if (totalTime > 5) { + + // if we've been unsubscribed for SWITCH_SUBSCRIPTION_TIME seconds, subscribe + if (!subscribed && unsubscribedForTime > SWITCH_SUBSCRIPTION_TIME) { + print("---- subscribing ----"); + Messages.subscribe(channel); + subscribed = true; + subscribedForTime = 0; + } + + // if we've been subscribed for SWITCH_SUBSCRIPTION_TIME seconds, unsubscribe + if (subscribed && subscribedForTime > SWITCH_SUBSCRIPTION_TIME) { + print("---- unsubscribing ----"); + Messages.unsubscribe(channel); + subscribed = false; + unsubscribedForTime = 0; + } + + // Even if not subscribed, still publish + var message = "update() deltaTime:" + deltaTime; + //print(message); + Messages.sendMessage(channel, message); + } +}); \ No newline at end of file From 034debc4833d42104379c3d233ceb2fc3ffefd0f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 10:59:53 -0800 Subject: [PATCH 0959/1003] cleanup --- .../src/messages/MessagesMixer.cpp | 77 ++----------------- .../src/messages/MessagesMixer.h | 11 --- .../resources/describe-settings.json | 6 +- .../src/DomainServerSettingsManager.cpp | 3 - libraries/networking/src/MessagesClient.cpp | 2 +- 5 files changed, 11 insertions(+), 88 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 8f69b86367..1c4ddb564b 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -34,14 +34,7 @@ const int MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND = 60; const unsigned int MESSAGES_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000; MessagesMixer::MessagesMixer(NLPacket& packet) : - ThreadedAssignment(packet), - _lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()), - _trailingSleepRatio(1.0f), - _performanceThrottlingRatio(0.0f), - _sumListeners(0), - _numStatFrames(0), - _sumBillboardPackets(0), - _sumIdentityPackets(0) + ThreadedAssignment(packet) { // make sure we hear about node kills so we can tell the other nodes connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &MessagesMixer::nodeKilled); @@ -55,43 +48,14 @@ MessagesMixer::MessagesMixer(NLPacket& packet) : MessagesMixer::~MessagesMixer() { } -// An 80% chance of sending a identity packet within a 5 second interval. -// assuming 60 htz update rate. -const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 187.0f; - void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { qDebug() << "MessagesMixer::nodeKilled()... node:" << killedNode->getUUID(); - - if (killedNode->getType() == NodeType::Agent - && killedNode->getLinkedData()) { - auto nodeList = DependencyManager::get(); - - // we also want to remove sequence number data for this avatar on our other avatars - // so invoke the appropriate method on the MessagesMixerClientData for other avatars - nodeList->eachMatchingNode( - [&](const SharedNodePointer& node)->bool { - if (!node->getLinkedData()) { - return false; - } - - if (node->getUUID() == killedNode->getUUID()) { - return false; - } - - return true; - }, - [&](const SharedNodePointer& node) { - qDebug() << "eachMatchingNode()... node:" << node->getUUID(); - } - ); - } + // FIXME - remove the node from the subscription maps } void MessagesMixer::handleMessages(QSharedPointer packetList, SharedNodePointer senderNode) { Q_ASSERT(packetList->getType() == PacketType::MessagesData); - qDebug() << "MessagesMixer::handleMessages()... senderNode:" << senderNode->getUUID(); - QByteArray packetData = packetList->getMessage(); QBuffer packet{ &packetData }; packet.open(QIODevice::ReadOnly); @@ -109,14 +73,6 @@ void MessagesMixer::handleMessages(QSharedPointer packetList, Shar auto nodeList = DependencyManager::get(); - qDebug() << "got a messages:" << message << "on channel:" << channel << "from node:" << senderNode->getUUID(); - - // this was an avatar we were sending to other people - // send a kill packet for it to our other nodes - //auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID); - //killPacket->write(killedNode->getUUID().toRfc4122()); - //nodeList->broadcastToNodes(std::move(killPacket), NodeSet() << NodeType::Agent); - nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { @@ -125,8 +81,6 @@ void MessagesMixer::handleMessages(QSharedPointer packetList, Shar }, [&](const SharedNodePointer& node) { - qDebug() << "sending a messages:" << message << "on channel:" << channel << "to node:" << node->getUUID(); - auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); auto channelUtf8 = channel.toUtf8(); @@ -163,47 +117,37 @@ void MessagesMixer::handleMessagesUnsubscribe(QSharedPointer packe // FIXME - make these stats relevant void MessagesMixer::sendStatsPacket() { QJsonObject statsObject; - statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; - statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100; - statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; - QJsonObject messagesObject; - auto nodeList = DependencyManager::get(); // add stats for each listerner nodeList->eachNode([&](const SharedNodePointer& node) { QJsonObject messagesStats; - const QString NODE_OUTBOUND_KBPS_STAT_KEY = "outbound_kbps"; - const QString NODE_INBOUND_KBPS_STAT_KEY = "inbound_kbps"; - // add the key to ask the domain-server for a username replacement, if it has it messagesStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); - messagesStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); - messagesStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth(); + messagesStats["outbound_kbps"] = node->getOutboundBandwidth(); + messagesStats["inbound_kbps"] = node->getInboundBandwidth(); messagesObject[uuidStringWithoutCurlyBraces(node->getUUID())] = messagesStats; }); statsObject["messages"] = messagesObject; ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); - - _sumListeners = 0; - _numStatFrames = 0; } void MessagesMixer::run() { ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); NodeType_t owningNodeType = DependencyManager::get()->getOwnerType(); - qDebug() << "owningNodeType:" << owningNodeType; auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); + /* nodeList->linkedDataCreateCallback = [] (Node* node) { // no need to link data }; + */ // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); @@ -228,13 +172,6 @@ void MessagesMixer::run() { } void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { - qDebug() << "MessagesMixer::parseDomainServerSettings() domainSettings:" << domainSettings; - const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; - // TODO - if we want options, parse them here... - // - // QJsonValue nodeBandwidthValue = domainSettings[MESSAGES_MIXER_SETTINGS_KEY].toObject()[NODE_SEND_BANDWIDTH_KEY]; - // if (!nodeBandwidthValue.isDouble()) { - // qDebug() << NODE_SEND_BANDWIDTH_KEY << "is not a double - will continue with default value"; - // } + const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index cd15ea7d2a..12667bcc1b 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -37,18 +37,7 @@ private slots: private: void parseDomainServerSettings(const QJsonObject& domainSettings); - QHash> _channelSubscribers; - - quint64 _lastFrameTimestamp; - - float _trailingSleepRatio; - float _performanceThrottlingRatio; - - int _sumListeners; - int _numStatFrames; - int _sumBillboardPackets; - int _sumIdentityPackets; }; #endif // hifi_MessagesMixer_h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index b2443b8bd4..c2410a1d0c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -556,10 +556,10 @@ "assignment-types": [4], "settings": [ { - "name": "max_node_send_bandwidth", + "name": "unused", "type": "double", - "label": "Per-Node Bandwidth", - "help": "Desired maximum send bandwidth (in Megabits per second) to each node", + "label": "Unused setting", + "help": "an asofyet unused setting", "placeholder": 1.0, "default": 1.0, "advanced": true diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 924e19e1fc..88fc6a6cad 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -132,7 +132,6 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList } QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) { - qDebug() << "DomainServerSettingsManager::valueOrDefaultValueForKeyPath() keyPath:" << keyPath; const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath); if (foundValue) { @@ -227,8 +226,6 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection } QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& typeValue, bool isAuthenticated) { - qDebug() << "DomainServerSettingsManager::responseObjectForType() typeValue:" << typeValue; - QJsonObject responseObject; if (!typeValue.isEmpty() || isAuthenticated) { diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 175c31beb8..9823a7130e 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -137,5 +137,5 @@ void MessagesClient::handleNodeKilled(SharedNodePointer node) { if (node->getType() != NodeType::MessagesMixer) { return; } - + // FIXME - do we need to do any special bookkeeping for when the messages mixer is no longer available } From 60ae1259fa7de0239eae1f5b8b8434e04c8158b4 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 11:06:27 -0800 Subject: [PATCH 0960/1003] more cleanup work --- libraries/networking/src/MessagesClient.cpp | 21 ------------------- libraries/networking/src/MessagesClient.h | 2 -- libraries/networking/src/PacketReceiver.cpp | 4 +--- .../networking/src/ThreadedAssignment.cpp | 3 --- 4 files changed, 1 insertion(+), 29 deletions(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 9823a7130e..60f1146c92 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -14,18 +14,11 @@ #include #include -#include #include -#include -#include "AssetRequest.h" -#include "AssetUpload.h" -#include "AssetUtils.h" -#include "NetworkAccessManager.h" #include "NetworkLogging.h" #include "NodeList.h" #include "PacketReceiver.h" -#include "ResourceCache.h" MessagesClient::MessagesClient() { @@ -45,20 +38,6 @@ void MessagesClient::init() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "init", Qt::BlockingQueuedConnection); } - - // Setup disk cache if not already - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - if (!networkAccessManager.cache()) { - QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - cachePath = !cachePath.isEmpty() ? cachePath : "interfaceCache"; - - QNetworkDiskCache* cache = new QNetworkDiskCache(); - cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE); - cache->setCacheDirectory(cachePath); - networkAccessManager.setCache(cache); - qCDebug(asset_client) << "MessagesClient disk cache setup at" << cachePath - << "(size:" << MAXIMUM_CACHE_SIZE / BYTES_PER_GIGABYTES << "GB)"; - } } bool haveMessagesMixer() { diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index 2c699850f6..a79b855be9 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -35,8 +35,6 @@ public: private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); void handleNodeKilled(SharedNodePointer node); - -private: }; #endif diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 81d8c5ee73..0a3ea86399 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -95,8 +95,6 @@ void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, } bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, const char* slot) { - qCDebug(networking) << "PacketReceiver::registerMessageListener() packet list type" << type; - Q_ASSERT_X(listener, "PacketReceiver::registerMessageListener", "No object to register"); Q_ASSERT_X(slot, "PacketReceiver::registerMessageListener", "No slot to register"); @@ -117,7 +115,7 @@ bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, return true; } else { - qCDebug(networking) << "NOT Registering a packet listener for packet list type" << type; + qCWarning(networking) << "FAILED to Register a packet listener for packet list type" << type; return false; } } diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index b204982896..6855c2eec3 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -124,12 +124,9 @@ void ThreadedAssignment::stopSendingStats() { } void ThreadedAssignment::checkInWithDomainServerOrExit() { - qDebug() << "ThreadedAssignment::checkInWithDomainServerOrExit()...."; if (DependencyManager::get()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { - qDebug() << "ThreadedAssignment::checkInWithDomainServerOrExit().... getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS"; setFinished(true); } else { - qDebug() << "ThreadedAssignment::checkInWithDomainServerOrExit().... calling DependencyManager::get()->sendDomainServerCheckIn()"; DependencyManager::get()->sendDomainServerCheckIn(); } } From 1c2c37ff44a95ae675fbca4eb2d4ec58c51664e3 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 11:10:12 -0800 Subject: [PATCH 0961/1003] more cleanup work --- assignment-client/src/messages/MessagesMixer.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 1c4ddb564b..b97afad85f 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -30,9 +30,6 @@ const QString MESSAGES_MIXER_LOGGING_NAME = "messages-mixer"; -const int MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND = 60; -const unsigned int MESSAGES_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) MESSAGES_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000; - MessagesMixer::MessagesMixer(NLPacket& packet) : ThreadedAssignment(packet) { @@ -143,12 +140,6 @@ void MessagesMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - /* - nodeList->linkedDataCreateCallback = [] (Node* node) { - // no need to link data - }; - */ - // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = nodeList->getDomainHandler(); From e7a8df306c881e44ef0cfb5bf6734d7f5924f139 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 11:18:36 -0800 Subject: [PATCH 0962/1003] fix warnings --- assignment-client/src/entities/EntityServer.cpp | 1 - assignment-client/src/messages/MessagesMixer.cpp | 2 -- interface/src/avatar/Head.cpp | 1 - interface/src/avatar/SkeletonModel.cpp | 1 - libraries/audio-client/src/AudioClient.cpp | 7 +------ libraries/recording/src/recording/impl/BufferClip.h | 3 ++- libraries/shared/src/shared/JSONHelpers.cpp | 1 - 7 files changed, 3 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 5754a9e057..2fafaa6731 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -112,7 +112,6 @@ int EntityServer::sendSpecialPackets(const SharedNodePointer& node, OctreeQueryN quint64 deletePacketSentAt = usecTimestampNow(); EntityTreePointer tree = std::static_pointer_cast(_tree); auto recentlyDeleted = tree->getRecentlyDeletedEntityIDs(); - bool hasMoreToSend = true; packetsSent = 0; diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index b97afad85f..ae1ac74306 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -135,8 +135,6 @@ void MessagesMixer::sendStatsPacket() { void MessagesMixer::run() { ThreadedAssignment::commonInit(MESSAGES_MIXER_LOGGING_NAME, NodeType::MessagesMixer); - NodeType_t owningNodeType = DependencyManager::get()->getOwnerType(); - auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b8cf8ab4f1..e8452583fc 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -91,7 +91,6 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { } if (isMine) { - MyAvatar* myAvatar = static_cast(_owningAvatar); auto player = DependencyManager::get(); // Only use face trackers when not playing back a recording. if (!player->isPlaying()) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 83c8cdfcf5..87f0e631f2 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -247,7 +247,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { return; // only simulate for own avatar } - MyAvatar* myAvatar = static_cast(_owningAvatar); auto player = DependencyManager::get(); if (player->isPlaying()) { return; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index a506fe217c..50bfd995f2 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -909,13 +909,8 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { // we don't have an audioPacket yet - set that up now _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); } + // FIXME either discard stereo in the recording or record a stereo flag - const int numNetworkBytes = _isStereoInput - ? AudioConstants::NETWORK_FRAME_BYTES_STEREO - : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - const int numNetworkSamples = _isStereoInput - ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO - : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); diff --git a/libraries/recording/src/recording/impl/BufferClip.h b/libraries/recording/src/recording/impl/BufferClip.h index af8a64716b..1ea79f3df2 100644 --- a/libraries/recording/src/recording/impl/BufferClip.h +++ b/libraries/recording/src/recording/impl/BufferClip.h @@ -26,7 +26,8 @@ public: private: virtual FrameConstPointer readFrame(size_t index) const override; QString _name { QUuid().toString() }; - mutable size_t _frameIndex { 0 }; + + //mutable size_t _frameIndex { 0 }; // FIXME - not in use }; } diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index 52ece73490..c0a8820d95 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -27,7 +27,6 @@ QJsonValue glmToJson(const T& t) { template T glmFromJson(const QJsonValue& json) { - static const T DEFAULT_VALUE = T(); T result; if (json.isArray()) { QJsonArray array = json.toArray(); From 5b9791d8002e79e78707f6d7f10df7721fe8908e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 11:50:10 -0800 Subject: [PATCH 0963/1003] add message received signal --- .../src/messages/MessagesMixer.cpp | 11 ------ examples/example/messagesExample.js | 5 +++ libraries/networking/src/DomainHandler.cpp | 2 -- libraries/networking/src/MessagesClient.cpp | 36 ++++++++----------- libraries/networking/src/MessagesClient.h | 3 ++ 5 files changed, 22 insertions(+), 35 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index ae1ac74306..21e3fdc4c5 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -9,22 +9,13 @@ // 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 "MessagesMixer.h" @@ -46,7 +37,6 @@ MessagesMixer::~MessagesMixer() { } void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { - qDebug() << "MessagesMixer::nodeKilled()... node:" << killedNode->getUUID(); // FIXME - remove the node from the subscription maps } @@ -67,7 +57,6 @@ void MessagesMixer::handleMessages(QSharedPointer packetList, Shar auto messageData = packet.read(messageLength); QString message = QString::fromUtf8(messageData); - auto nodeList = DependencyManager::get(); nodeList->eachMatchingNode( diff --git a/examples/example/messagesExample.js b/examples/example/messagesExample.js index 39ee4a3dbe..11827f019f 100644 --- a/examples/example/messagesExample.js +++ b/examples/example/messagesExample.js @@ -35,4 +35,9 @@ Script.update.connect(function (deltaTime) { //print(message); Messages.sendMessage(channel, message); } +}); + + +Messages.messageReceived.connect(function (channel, message) { + print("message received on channel:" + channel + ", message:" + message); }); \ No newline at end of file diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index afb2dde266..f7d26f25c5 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -260,8 +260,6 @@ void DomainHandler::requestDomainSettings() { Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get()->getOwnerType()); - qCDebug(networking) << "Requesting settings from domain server for assignmentType:" << assignmentType; - auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false); packet->writePrimitive(assignmentType); diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 60f1146c92..1f9a8c123c 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -40,31 +40,25 @@ void MessagesClient::init() { } } -bool haveMessagesMixer() { - auto nodeList = DependencyManager::get(); - SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); - - if (!messagesMixer) { - qCWarning(messages_client) << "Could not complete MessagesClient operation " - << "since you are not currently connected to a messages-mixer."; - return false; - } - - return true; -} - void MessagesClient::handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode) { - QByteArray data = packetList->getMessage(); - auto packetType = packetList->getType(); + QByteArray packetData = packetList->getMessage(); + QBuffer packet{ &packetData }; + packet.open(QIODevice::ReadOnly); - if (packetType == PacketType::MessagesData) { - QString message = QString::fromUtf8(data); - qDebug() << "got a messages packet:" << message; - } + quint16 channelLength; + packet.read(reinterpret_cast(&channelLength), sizeof(channelLength)); + auto channelData = packet.read(channelLength); + QString channel = QString::fromUtf8(channelData); + + quint16 messageLength; + packet.read(reinterpret_cast(&messageLength), sizeof(messageLength)); + auto messageData = packet.read(messageLength); + QString message = QString::fromUtf8(messageData); + + emit messageReceived(channel, message); } void MessagesClient::sendMessage(const QString& channel, const QString& message) { - qDebug() << "MessagesClient::sendMessage() channel:" << channel << "message:" << message; auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); @@ -89,7 +83,6 @@ void MessagesClient::sendMessage(const QString& channel, const QString& message) // in the event that they mixer goes away and/or comes back we should automatically // resubscribe to those channels void MessagesClient::subscribe(const QString& channel) { - qDebug() << "MessagesClient::subscribe() channel:" << channel; auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); @@ -101,7 +94,6 @@ void MessagesClient::subscribe(const QString& channel) { } void MessagesClient::unsubscribe(const QString& channel) { - qDebug() << "MessagesClient::unsubscribe() channel:" << channel; auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index a79b855be9..13e908e129 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -32,6 +32,9 @@ public: Q_INVOKABLE void subscribe(const QString& channel); Q_INVOKABLE void unsubscribe(const QString& channel); +signals: + void messageReceived(const QString& channel, const QString& message); + private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); void handleNodeKilled(SharedNodePointer node); From 46c8d7b3f84d51f114cc87bdd7141ffca03929a3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 12:32:45 -0800 Subject: [PATCH 0964/1003] fix for release build undeclared identifier --- plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index 3898d586ad..ddf251778f 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -167,10 +167,7 @@ void OculusLegacyDisplayPlugin::activate() { } }); - #ifndef QT_NO_DEBUG - ovrBool result = - #endif - ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); + ovrBool result = ovrHmd_ConfigureRendering(_hmd, &config.Config, distortionCaps, _eyeFovs, _eyeRenderDescs); Q_ASSERT(result); } From 32bf81ef0db6d46dfcb79d1d782fc23997f70dbe Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 12:59:12 -0800 Subject: [PATCH 0965/1003] move MessagesClient to agent --- assignment-client/src/Agent.cpp | 20 +++++++++++--------- assignment-client/src/Agent.h | 1 - interface/src/Application.cpp | 1 - libraries/script-engine/src/ScriptEngine.cpp | 5 +++++ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0d719d6806..1f56118177 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -55,7 +56,6 @@ Agent::Agent(NLPacket& packet) : { PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase }, this, "handleOctreePacket"); packetReceiver.registerListener(PacketType::Jurisdiction, this, "handleJurisdictionPacket"); - packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagePacket"); } void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode) { @@ -96,14 +96,6 @@ void Agent::handleJurisdictionPacket(QSharedPointer packet, SharedNode } } -void Agent::handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode) { - auto packetType = packet->getType(); - - if (packetType == PacketType::MessagesData) { - qDebug() << "got a messages packet"; - } -} - void Agent::handleAudioPacket(QSharedPointer packet) { _receivedAudioStream.parseData(*packet); @@ -118,11 +110,21 @@ const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); + // Setup MessagesClient + auto messagesClient = DependencyManager::set(); + QThread* messagesThread = new QThread; + messagesThread->setObjectName("Messages Client Thread"); + messagesClient->moveToThread(messagesThread); + connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init); + messagesThread->start(); + + auto nodeList = DependencyManager::get(); nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer + << NodeType::MessagesMixer ); _pingTimer = new QTimer(this); diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index be3a0db293..ab000015d5 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,7 +58,6 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); - void handleMessagesPacket(QSharedPointer packet, SharedNodePointer senderNode); void sendPingRequests(); void processAgentAvatarAndAudio(float deltaTime); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 712fb7dc02..c77bb9a114 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4029,7 +4029,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Messages", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0f62bf8cd5..611be863c2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -375,6 +376,10 @@ void ScriptEngine::init() { auto scriptingInterface = DependencyManager::get(); registerGlobalObject("Controller", scriptingInterface.data()); UserInputMapper::registerControllerTypes(this); + + + registerGlobalObject("Messages", DependencyManager::get().data()); + } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From d21a2fee203b087d952bd6f76696668978a175ff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 13:12:22 -0800 Subject: [PATCH 0966/1003] don't use spatial-key for normal grab, only equip. allow switching from a near or far grab to an equip. --- examples/controllers/handControllerGrab.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index c50a1418bd..add809e73b 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -606,6 +606,16 @@ function MyController(hand) { 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, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() && + typeof grabbableData.spatialKey !== 'undefined') { + var saveGrabbedID = this.grabbedEntity; + this.release(); + this.setState(STATE_EQUIP); + this.grabbedEntity = saveGrabbedID; + return; + } this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); @@ -710,7 +720,8 @@ function MyController(hand) { var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - if (grabbableData.spatialKey) { + if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) { + // if an object is "equipped" and has a spatialKey, use it. if (grabbableData.spatialKey.relativePosition) { this.offsetPosition = grabbableData.spatialKey.relativePosition; } @@ -770,6 +781,10 @@ function MyController(hand) { this.setState(STATE_WAITING_FOR_BUMPER_RELEASE); return; } + if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) { + this.setState(STATE_CONTINUE_EQUIP_BD); + return; + } // Keep track of the fingertip velocity to impart when we release the object. // Note that the idea of using a constant 'tip' velocity regardless of the From 2d3fe497e46c6dc7502543040ed18bfd93f1c112 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 13:29:28 -0800 Subject: [PATCH 0967/1003] fix typo --- libraries/networking/src/MessagesClient.cpp | 6 +----- libraries/networking/src/PacketReceiver.cpp | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 1f9a8c123c..ac2bf55033 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -21,16 +21,12 @@ #include "PacketReceiver.h" MessagesClient::MessagesClient() { - setCustomDeleter([](Dependency* dependency){ static_cast(dependency)->deleteLater(); }); - auto nodeList = DependencyManager::get(); auto& packetReceiver = nodeList->getPacketReceiver(); - - packetReceiver.registerListener(PacketType::MessagesData, this, "handleMessagePacket"); - + packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacket"); connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &MessagesClient::handleNodeKilled); } diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index 0a3ea86399..07f25fee5f 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -97,7 +97,7 @@ void PacketReceiver::registerDirectListenerForTypes(PacketTypeList types, bool PacketReceiver::registerMessageListener(PacketType type, QObject* listener, const char* slot) { Q_ASSERT_X(listener, "PacketReceiver::registerMessageListener", "No object to register"); Q_ASSERT_X(slot, "PacketReceiver::registerMessageListener", "No slot to register"); - + QMetaMethod matchingMethod = matchingMethodForListener(type, listener, slot); if (matchingMethod.isValid()) { From 2f142eb0887e2ff6315171b6a66fa3b33c711ce1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 13:33:30 -0800 Subject: [PATCH 0968/1003] add a receiver example --- examples/example/messagesReceiverExample.js | 22 ++++++++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 5 +---- 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 examples/example/messagesReceiverExample.js diff --git a/examples/example/messagesReceiverExample.js b/examples/example/messagesReceiverExample.js new file mode 100644 index 0000000000..31020a4c8a --- /dev/null +++ b/examples/example/messagesReceiverExample.js @@ -0,0 +1,22 @@ +var totalTime = 0; +var subscribed = false; +var WAIT_FOR_SUBSCRIPTION_TIME = 10; +function myUpdate(deltaTime) { + var channel = "example"; + totalTime += deltaTime; + + if (totalTime > WAIT_FOR_SUBSCRIPTION_TIME && !subscribed) { + + print("---- subscribing ----"); + Messages.subscribe(channel); + subscribed = true; + Script.update.disconnect(myUpdate); + } +} + +Script.update.connect(myUpdate); + +Messages.messageReceived.connect(function (channel, message) { + print("message received on channel:" + channel + ", message:" + message); +}); + diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 611be863c2..c17b091643 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -367,6 +367,7 @@ void ScriptEngine::init() { registerGlobalObject("Vec3", &_vec3Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("AnimationCache", DependencyManager::get().data()); + registerGlobalObject("Messages", DependencyManager::get().data()); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); @@ -376,10 +377,6 @@ void ScriptEngine::init() { auto scriptingInterface = DependencyManager::get(); registerGlobalObject("Controller", scriptingInterface.data()); UserInputMapper::registerControllerTypes(this); - - - registerGlobalObject("Messages", DependencyManager::get().data()); - } void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { From f54374b66abb8d07890ccf0b3eb62d3b99e99234 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 13:40:04 -0800 Subject: [PATCH 0969/1003] remove unused settings --- domain-server/resources/describe-settings.json | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index c2410a1d0c..e0038117f0 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -549,22 +549,6 @@ "advanced": true } ] - }, - { - "name": "messages_mixer", - "label": "Messages Mixer", - "assignment-types": [4], - "settings": [ - { - "name": "unused", - "type": "double", - "label": "Unused setting", - "help": "an asofyet unused setting", - "placeholder": 1.0, - "default": 1.0, - "advanced": true - } - ] } ] } From e93b5c5838614e37a67fd9dceb9739954217e7c0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 17 Nov 2015 14:02:27 -0800 Subject: [PATCH 0970/1003] Bug fixes for avatars with no eyes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed default eye position to 1.9 meters because the hifi_team avatars are 2.0 meters tall. Also, prevent array access with negative indices when eye bones are missing. ಠ_ಠ --- interface/src/avatar/MyAvatar.cpp | 68 ++++++++++++++++++++++++ libraries/animation/src/AnimSkeleton.cpp | 2 +- libraries/animation/src/Rig.cpp | 34 +++++++----- 3 files changed, 91 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 38eb5042f7..2eb005fc1c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -379,6 +379,13 @@ void MyAvatar::updateHMDFollowVelocity() { // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. void MyAvatar::updateSensorToWorldMatrix() { + +#ifdef DEBUG_RENDERING + // draw marker about avatar's position + const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); + DebugDraw::getInstance().addMyAvatarMarker("pos", glm::quat(), glm::vec3(), red); +#endif + // update the sensor mat so that the body position will end up in the desired // position when driven from the head. glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition()); @@ -1859,6 +1866,7 @@ glm::quat MyAvatar::getWorldBodyOrientation() const { return glm::quat_cast(_sensorToWorldMatrix * _bodySensorMatrix); } +#if 0 // derive avatar body position and orientation from the current HMD Sensor location. // results are in sensor space glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { @@ -1876,6 +1884,66 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { } return glm::mat4(); } +#else +// old school meat hook style +glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { + + // HMD is in sensor space. + const glm::vec3 hmdPosition = getHMDSensorPosition(); + const glm::quat hmdOrientation = getHMDSensorOrientation(); + const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); + + /* + const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); + const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); + const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); + const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f); + */ + + // 2 meter tall dude + const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f); + const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f); + const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); + const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f); + + vec3 localEyes, localNeck; + if (!_debugDrawSkeleton) { + const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); + localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); + localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS); + } else { + // TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses. + // for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig. + + // TODO: cache these indices. + int rightEyeIndex = _debugDrawSkeleton->nameToJointIndex("RightEye"); + int leftEyeIndex = _debugDrawSkeleton->nameToJointIndex("LeftEye"); + int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); + int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); + + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; + glm::vec3 absHipsPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS; + + const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); + localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); + localNeck = rotY180 * (absNeckPos - absHipsPos); + } + + // apply simplistic head/neck model + // figure out where the avatar body should be by applying offsets from the avatar's neck & head joints. + + // eyeToNeck offset is relative full HMD orientation. + // while neckToRoot offset is only relative to HMDs yaw. + glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes); + glm::vec3 neckToRoot = hmdOrientationYawOnly * -localNeck; + glm::vec3 bodyPos = hmdPosition + eyeToNeck + neckToRoot; + + // avatar facing is determined solely by hmd orientation. + return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos); +} +#endif glm::vec3 MyAvatar::getPositionForAudio() { switch (_audioListenerMode) { diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 0db7473c9c..7ec8db1490 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -79,7 +79,7 @@ const QString& AnimSkeleton::getJointName(int jointIndex) const { } AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const { - if (jointIndex < 0) { + if (jointIndex < 0 || jointIndex >= (int)poses.size() || jointIndex >= (int)_joints.size()) { return AnimPose::identity; } else { return getAbsolutePose(_joints[jointIndex].parentIndex, poses) * poses[jointIndex]; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9b6221a370..90f068d1ef 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -23,6 +23,19 @@ #include "AnimSkeleton.h" #include "IKTarget.h" +/* +const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); +const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); +const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f); +const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); +*/ + +// 2 meter tall dude +const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f); +const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f); +const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.75f, 0.0f); +const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); + void insertSorted(QList& handles, const AnimationHandlePointer& handle) { for (QList::iterator it = handles.begin(); it != handles.end(); it++) { if (handle->getPriority() > (*it)->getPriority()) { @@ -410,17 +423,19 @@ void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) { // TODO: use cached eye/hips indices for these calculations int numPoses = poses.size(); - int rightEyeIndex = _animSkeleton->nameToJointIndex(QString("RightEye")); - int leftEyeIndex = _animSkeleton->nameToJointIndex(QString("LeftEye")); - if (numPoses > rightEyeIndex && numPoses > leftEyeIndex - && rightEyeIndex > 0 && leftEyeIndex > 0) { - int hipsIndex = _animSkeleton->nameToJointIndex(QString("Hips")); - int headIndex = _animSkeleton->nameToJointIndex(QString("Head")); - if (hipsIndex >= 0 && headIndex > 0) { + int hipsIndex = _animSkeleton->nameToJointIndex(QString("Hips")); + int headIndex = _animSkeleton->nameToJointIndex(QString("Head")); + if (hipsIndex > 0 && headIndex > 0) { + int rightEyeIndex = _animSkeleton->nameToJointIndex(QString("RightEye")); + int leftEyeIndex = _animSkeleton->nameToJointIndex(QString("LeftEye")); + if (numPoses > rightEyeIndex && numPoses > leftEyeIndex && rightEyeIndex > 0 && leftEyeIndex > 0) { glm::vec3 rightEye = _animSkeleton->getAbsolutePose(rightEyeIndex, poses).trans; glm::vec3 leftEye = _animSkeleton->getAbsolutePose(leftEyeIndex, poses).trans; glm::vec3 hips = _animSkeleton->getAbsolutePose(hipsIndex, poses).trans; _eyesInRootFrame = 0.5f * (rightEye + leftEye) - hips; + } else { + glm::vec3 hips = _animSkeleton->getAbsolutePose(hipsIndex, poses).trans; + _eyesInRootFrame = 0.5f * (DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) - hips; } } } @@ -1172,11 +1187,6 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A int headIndex = skeleton->nameToJointIndex("Head"); int neckIndex = skeleton->nameToJointIndex("Neck"); - const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); - const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); - const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f); - const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); - // Use absolute bindPose positions just in case the relBindPose have rotations we don't expect. glm::vec3 absRightEyePos = rightEyeIndex != -1 ? skeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? skeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; From 8112b3b57e57d95558caa2a77fba8fa76ed78241 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 14:16:22 -0800 Subject: [PATCH 0971/1003] add senderUUID to the messageReceived signal --- examples/example/messagesExample.js | 4 ++-- examples/example/messagesReceiverExample.js | 5 ++--- libraries/networking/src/MessagesClient.cpp | 2 +- libraries/networking/src/MessagesClient.h | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/example/messagesExample.js b/examples/example/messagesExample.js index 11827f019f..f5d8dea2d3 100644 --- a/examples/example/messagesExample.js +++ b/examples/example/messagesExample.js @@ -38,6 +38,6 @@ Script.update.connect(function (deltaTime) { }); -Messages.messageReceived.connect(function (channel, message) { - print("message received on channel:" + channel + ", message:" + message); +Messages.messageReceived.connect(function (channel, message, senderID) { + print("message received on channel:" + channel + ", message:" + message + ", senderID:" + senderID); }); \ No newline at end of file diff --git a/examples/example/messagesReceiverExample.js b/examples/example/messagesReceiverExample.js index 31020a4c8a..caab270783 100644 --- a/examples/example/messagesReceiverExample.js +++ b/examples/example/messagesReceiverExample.js @@ -16,7 +16,6 @@ function myUpdate(deltaTime) { Script.update.connect(myUpdate); -Messages.messageReceived.connect(function (channel, message) { - print("message received on channel:" + channel + ", message:" + message); +Messages.messageReceived.connect(function (channel, message, senderID) { + print("message received on channel:" + channel + ", message:" + message + ", senderID:" + senderID); }); - diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index ac2bf55033..81e9c3abdf 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -51,7 +51,7 @@ void MessagesClient::handleMessagesPacket(QSharedPointer packetLis auto messageData = packet.read(messageLength); QString message = QString::fromUtf8(messageData); - emit messageReceived(channel, message); + emit messageReceived(channel, message, senderNode->getUUID()); } void MessagesClient::sendMessage(const QString& channel, const QString& message) { diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index 13e908e129..4eb63c3e74 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -33,7 +33,7 @@ public: Q_INVOKABLE void unsubscribe(const QString& channel); signals: - void messageReceived(const QString& channel, const QString& message); + void messageReceived(const QString& channel, const QString& message, const QUuid& senderUUID); private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); From 6dfcc53c27c175e21f03863a7ffa783562072573 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 14:19:13 -0800 Subject: [PATCH 0972/1003] properly handle removing subscribers from channels when the subscriber node disconnects --- assignment-client/src/messages/MessagesMixer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 21e3fdc4c5..99798b2d4f 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -37,7 +37,9 @@ MessagesMixer::~MessagesMixer() { } void MessagesMixer::nodeKilled(SharedNodePointer killedNode) { - // FIXME - remove the node from the subscription maps + for (auto& channel : _channelSubscribers) { + channel.remove(killedNode->getUUID()); + } } void MessagesMixer::handleMessages(QSharedPointer packetList, SharedNodePointer senderNode) { From 85aa3b3f83d9412aaf4dc7dffb07fdfff789e7fa Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 14:28:51 -0800 Subject: [PATCH 0973/1003] handle subscribe when messages mixer is not available --- examples/example/messagesExample.js | 40 ++++++++++----------- examples/example/messagesReceiverExample.js | 20 ++--------- libraries/networking/src/MessagesClient.cpp | 16 ++++----- libraries/networking/src/MessagesClient.h | 5 ++- 4 files changed, 33 insertions(+), 48 deletions(-) diff --git a/examples/example/messagesExample.js b/examples/example/messagesExample.js index f5d8dea2d3..390549a135 100644 --- a/examples/example/messagesExample.js +++ b/examples/example/messagesExample.js @@ -12,29 +12,25 @@ Script.update.connect(function (deltaTime) { subscribedForTime += deltaTime; } - if (totalTime > 5) { - - // if we've been unsubscribed for SWITCH_SUBSCRIPTION_TIME seconds, subscribe - if (!subscribed && unsubscribedForTime > SWITCH_SUBSCRIPTION_TIME) { - print("---- subscribing ----"); - Messages.subscribe(channel); - subscribed = true; - subscribedForTime = 0; - } - - // if we've been subscribed for SWITCH_SUBSCRIPTION_TIME seconds, unsubscribe - if (subscribed && subscribedForTime > SWITCH_SUBSCRIPTION_TIME) { - print("---- unsubscribing ----"); - Messages.unsubscribe(channel); - subscribed = false; - unsubscribedForTime = 0; - } - - // Even if not subscribed, still publish - var message = "update() deltaTime:" + deltaTime; - //print(message); - Messages.sendMessage(channel, message); + // if we've been unsubscribed for SWITCH_SUBSCRIPTION_TIME seconds, subscribe + if (!subscribed && unsubscribedForTime > SWITCH_SUBSCRIPTION_TIME) { + print("---- subscribing ----"); + Messages.subscribe(channel); + subscribed = true; + subscribedForTime = 0; } + + // if we've been subscribed for SWITCH_SUBSCRIPTION_TIME seconds, unsubscribe + if (subscribed && subscribedForTime > SWITCH_SUBSCRIPTION_TIME) { + print("---- unsubscribing ----"); + Messages.unsubscribe(channel); + subscribed = false; + unsubscribedForTime = 0; + } + + // Even if not subscribed, still publish + var message = "update() deltaTime:" + deltaTime; + Messages.sendMessage(channel, message); }); diff --git a/examples/example/messagesReceiverExample.js b/examples/example/messagesReceiverExample.js index caab270783..2239ab1fc3 100644 --- a/examples/example/messagesReceiverExample.js +++ b/examples/example/messagesReceiverExample.js @@ -1,21 +1,7 @@ -var totalTime = 0; -var subscribed = false; -var WAIT_FOR_SUBSCRIPTION_TIME = 10; -function myUpdate(deltaTime) { - var channel = "example"; - totalTime += deltaTime; - - if (totalTime > WAIT_FOR_SUBSCRIPTION_TIME && !subscribed) { - - print("---- subscribing ----"); - Messages.subscribe(channel); - subscribed = true; - Script.update.disconnect(myUpdate); - } -} - -Script.update.connect(myUpdate); +print("---- subscribing ----"); +Messages.subscribe(channel); Messages.messageReceived.connect(function (channel, message, senderID) { print("message received on channel:" + channel + ", message:" + message + ", senderID:" + senderID); }); + diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 81e9c3abdf..88d64adcdd 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -27,7 +27,7 @@ MessagesClient::MessagesClient() { auto nodeList = DependencyManager::get(); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacket"); - connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &MessagesClient::handleNodeKilled); + connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &MessagesClient::handleNodeAdded); } void MessagesClient::init() { @@ -75,10 +75,8 @@ void MessagesClient::sendMessage(const QString& channel, const QString& message) } } -// FIXME - we should keep track of the channels we are subscribed to locally, and -// in the event that they mixer goes away and/or comes back we should automatically -// resubscribe to those channels void MessagesClient::subscribe(const QString& channel) { + _subscribedChannels << channel; auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); @@ -90,6 +88,7 @@ void MessagesClient::subscribe(const QString& channel) { } void MessagesClient::unsubscribe(const QString& channel) { + _subscribedChannels.remove(channel); auto nodeList = DependencyManager::get(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); @@ -100,9 +99,10 @@ void MessagesClient::unsubscribe(const QString& channel) { } } -void MessagesClient::handleNodeKilled(SharedNodePointer node) { - if (node->getType() != NodeType::MessagesMixer) { - return; +void MessagesClient::handleNodeAdded(SharedNodePointer node) { + if (node->getType() == NodeType::MessagesMixer) { + for (const auto& channel : _subscribedChannels) { + subscribe(channel); + } } - // FIXME - do we need to do any special bookkeeping for when the messages mixer is no longer available } diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index 4eb63c3e74..695e7e789e 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -37,7 +37,10 @@ signals: private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); - void handleNodeKilled(SharedNodePointer node); + void handleNodeAdded(SharedNodePointer node); + +protected: + QSet _subscribedChannels; }; #endif From d8a3927311ba83a9dab81f0ccc6bfec6418f300c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 14:43:06 -0800 Subject: [PATCH 0974/1003] debug the late connect case --- examples/example/messagesReceiverExample.js | 2 +- libraries/networking/src/MessagesClient.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/example/messagesReceiverExample.js b/examples/example/messagesReceiverExample.js index 2239ab1fc3..b9f9e54bca 100644 --- a/examples/example/messagesReceiverExample.js +++ b/examples/example/messagesReceiverExample.js @@ -1,5 +1,5 @@ print("---- subscribing ----"); -Messages.subscribe(channel); +Messages.subscribe("example"); Messages.messageReceived.connect(function (channel, message, senderID) { print("message received on channel:" + channel + ", message:" + message + ", senderID:" + senderID); diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 88d64adcdd..6c6ccbf25c 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -101,7 +101,9 @@ void MessagesClient::unsubscribe(const QString& channel) { void MessagesClient::handleNodeAdded(SharedNodePointer node) { if (node->getType() == NodeType::MessagesMixer) { + qDebug() << "messages-mixer node type added..."; for (const auto& channel : _subscribedChannels) { + qDebug() << "subscribing to channel:" << channel; subscribe(channel); } } From 3efbcb7062eab473b1a3889168dc74a3e14f0d4c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 14:47:59 -0800 Subject: [PATCH 0975/1003] debug the late connect case --- libraries/networking/src/MessagesClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 6c6ccbf25c..51cfba263c 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -84,6 +84,7 @@ void MessagesClient::subscribe(const QString& channel) { auto packetList = NLPacketList::create(PacketType::MessagesSubscribe, QByteArray(), true, true); packetList->write(channel.toUtf8()); nodeList->sendPacketList(std::move(packetList), *messagesMixer); + qDebug() << "sending MessagesSubscribe for channel:" << channel; } } From c6051bb3253234113d2323b9ca328e810b1a0bfc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 14:58:20 -0800 Subject: [PATCH 0976/1003] reduce size of near-grab radius. don't draw pick laser until we know we aren't going to do a near grab --- examples/controllers/handControllerGrab.js | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index add809e73b..973ca53cf6 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -47,7 +47,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // near grabbing // -var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected +var GRAB_RADIUS = 0.01; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected @@ -382,8 +382,6 @@ function MyController(hand) { length: PICK_MAX_DISTANCE }; - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - // don't pick 60x per second. var pickRays = []; var now = Date.now(); @@ -538,16 +536,17 @@ function MyController(hand) { grabbableData = grabbableDataForCandidate; } } - if (this.grabbedEntity === null) { - return; - } - if (grabbableData.wantsTrigger) { - this.setState(STATE_NEAR_TRIGGER); - return; - } else if (!props.locked && props.collisionsWillMove) { - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) - return; + if (this.grabbedEntity !== null) { + if (grabbableData.wantsTrigger) { + this.setState(STATE_NEAR_TRIGGER); + return; + } else if (!props.locked && props.collisionsWillMove) { + this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + return; + } } + + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); }; this.distanceHolding = function() { From 5ae3c5aea0429014863b018550a0137616540d2b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 14:59:06 -0800 Subject: [PATCH 0977/1003] adjust size of near-grab radius. --- 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 973ca53cf6..efd288c751 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -47,7 +47,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray // near grabbing // -var GRAB_RADIUS = 0.01; // if the ray misses but an object is this close, it will still be selected +var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected From 7dfdb3c72edf2f50f171a90f5ae8c2e991f6f57c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 15:04:14 -0800 Subject: [PATCH 0978/1003] protect LNL packet sending without active socket --- libraries/networking/src/LimitedNodeList.cpp | 39 ++++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index fdb5049f00..0d858ba3a3 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -299,14 +299,16 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiS qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& destinationNode) { Q_ASSERT(!packet->isPartOfMessage()); - if (!destinationNode.getActiveSocket()) { + auto activeSocket = destinationNode.getActiveSocket(); + if (!activeSocket) { + qDebug() << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending"; return 0; } emit dataSent(destinationNode.getType(), packet->getDataSize()); destinationNode.recordBytesSent(packet->getDataSize()); - return sendPacket(std::move(packet), *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret()); + return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret()); } qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, @@ -328,8 +330,11 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiS qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const Node& destinationNode) { auto activeSocket = destinationNode.getActiveSocket(); if (!activeSocket) { + qDebug() << "LimitedNodeList::sendPacketList called without active socket for node" << destinationNode + << " - not sending."; return 0; } + qint64 bytesSent = 0; auto connectionSecret = destinationNode.getConnectionSecret(); @@ -372,23 +377,35 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, } qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, const Node& destinationNode) { - // close the last packet in the list - packetList->closeCurrentPacket(); - - for (std::unique_ptr& packet : packetList->_packets) { - NLPacket* nlPacket = static_cast(packet.get()); - collectPacketStats(*nlPacket); - fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret()); + auto activeSocket = destinationNode.getActiveSocket(); + if (!activeSocket) { + // close the last packet in the list + packetList->closeCurrentPacket(); + + for (std::unique_ptr& packet : packetList->_packets) { + NLPacket* nlPacket = static_cast(packet.get()); + collectPacketStats(*nlPacket); + fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret()); + } + + return _nodeSocket.writePacketList(std::move(packetList), *activeSocket); + } else { + qCDebug(networking) << "LimitedNodeList::sendPacketList called without active socket for node. Not sending."; + return 0; } - - return _nodeSocket.writePacketList(std::move(packetList), *destinationNode.getActiveSocket()); } qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr) { + if (!overridenSockAddr.isNull() && !destinationNode.getActiveSocket()) { + qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node. Not sending."; + return 0; + } + // use the node's active socket as the destination socket if there is no overriden socket address auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket() : overridenSockAddr; + return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret()); } From 34b8fca83befa4c561dfbd40839f3915582a6b14 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 15:04:31 -0800 Subject: [PATCH 0979/1003] add socketActivated signal to NetworkPeer for punch success --- libraries/networking/src/NetworkPeer.cpp | 4 ++++ libraries/networking/src/NetworkPeer.h | 1 + 2 files changed, 5 insertions(+) diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index 9253243a7f..da2eced05c 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -113,6 +113,10 @@ void NetworkPeer::setActiveSocket(HifiSockAddr* discoveredSocket) { // we're now considered connected to this peer - reset the number of connection attemps resetConnectionAttempts(); + + if (_activeSocket) { + emit socketActivated(*_activeSocket); + } } void NetworkPeer::activateLocalSocket() { diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index c10d44bfa9..0011f3da76 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -83,6 +83,7 @@ public slots: void stopPingTimer(); signals: void pingTimerTimeout(); + void socketActivated(const HifiSockAddr& sockAddr); protected: void setActiveSocket(HifiSockAddr* discoveredSocket); From 900f425f359fe59300d109bb1b96523720ba61c3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 11:58:46 -0800 Subject: [PATCH 0980/1003] Recording fixes --- .../scripting/RecordingScriptingInterface.cpp | 15 +- libraries/audio-client/src/AudioClient.cpp | 176 +++++++++--------- libraries/audio-client/src/AudioClient.h | 1 + libraries/avatars/src/AvatarData.cpp | 11 +- 4 files changed, 94 insertions(+), 109 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 32bd6fde97..4ca3881e8d 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -162,26 +162,15 @@ void RecordingScriptingInterface::startRecording() { } _recordingEpoch = Frame::epochForFrameTime(0); - - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->setRecordingBasis(); + DependencyManager::get()->getMyAvatar()->setRecordingBasis(); _recorder->start(); } void RecordingScriptingInterface::stopRecording() { _recorder->stop(); - _lastClip = _recorder->getClip(); - // post-process the audio into discreet chunks based on times of received samples _lastClip->seek(0); - Frame::ConstPointer frame; - while (frame = _lastClip->nextFrame()) { - qDebug() << "Frame time " << frame->timeOffset << " size " << frame->data.size(); - } - _lastClip->seek(0); - - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->clearRecordingBasis(); + DependencyManager::get()->getMyAvatar()->clearRecordingBasis(); } void RecordingScriptingInterface::saveRecording(const QString& filename) { diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 50bfd995f2..d4e571ade5 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -743,19 +743,9 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - if (!_audioPacket) { - // we don't have an audioPacket yet - set that up now - _audioPacket = NLPacket::create(PacketType::MicrophoneAudioNoEcho); - } - const float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(); - const int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); - - static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); - int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); - QByteArray inputByteArray = _inputDevice->readAll(); // Add audio source injection if enabled @@ -784,30 +774,30 @@ void AudioClient::handleAudioInput() { float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsecsRead(audioInputMsecsRead); - while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { + const int numNetworkBytes = _isStereoInput + ? AudioConstants::NETWORK_FRAME_BYTES_STEREO + : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkSamples = _isStereoInput + ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO + : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; - const int numNetworkBytes = _isStereoInput - ? AudioConstants::NETWORK_FRAME_BYTES_STEREO - : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; - const int numNetworkSamples = _isStereoInput - ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO - : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; + + while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) { if (!_muted) { - // zero out the monoAudioSamples array and the locally injected audio - memset(networkAudioSamples, 0, numNetworkBytes); // Increment the time since the last clip if (_timeSinceLastClip >= 0.0f) { - _timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE; + _timeSinceLastClip += (float)numNetworkSamples / (float)AudioConstants::SAMPLE_RATE; } _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); possibleResampling(_inputToNetworkResampler, - inputAudioSamples.get(), networkAudioSamples, - inputSamplesRequired, numNetworkSamples, - _inputFormat, _desiredInputFormat); + inputAudioSamples.get(), networkAudioSamples, + inputSamplesRequired, numNetworkSamples, + _inputFormat, _desiredInputFormat); // Remove DC offset if (!_isStereoInput && !_audioSourceInjectEnabled) { @@ -829,7 +819,7 @@ void AudioClient::handleAudioInput() { for (int i = 0; i < numNetworkSamples; i++) { int thisSample = std::abs(networkAudioSamples[i]); - loudness += (float) thisSample; + loudness += (float)thisSample; if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) { _timeSinceLastClip = 0.0f; @@ -839,7 +829,7 @@ void AudioClient::handleAudioInput() { _lastInputLoudness = fabs(loudness / numNetworkSamples); } - emit inputReceived({reinterpret_cast(networkAudioSamples), numNetworkBytes}); + emit inputReceived({ reinterpret_cast(networkAudioSamples), numNetworkBytes }); } else { // our input loudness is 0, since we're muted @@ -849,14 +839,38 @@ void AudioClient::handleAudioInput() { _inputRingBuffer.shiftReadPosition(inputSamplesRequired); } - auto nodeList = DependencyManager::get(); - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + emitAudioPacket(networkAudioSamples); + } +} - if (audioMixer && audioMixer->getActiveSocket()) { - glm::vec3 headPosition = _positionGetter(); - glm::quat headOrientation = _orientationGetter(); - quint8 isStereo = _isStereoInput ? 1 : 0; +void AudioClient::emitAudioPacket(const int16_t* audioData, PacketType packetType) { + static std::mutex _mutex; + using Locker = std::unique_lock; + // FIXME recorded audio isn't guaranteed to have the same stereo state + // as the current system + const int numNetworkBytes = _isStereoInput + ? AudioConstants::NETWORK_FRAME_BYTES_STEREO + : AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL; + const int numNetworkSamples = _isStereoInput + ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO + : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; + + auto nodeList = DependencyManager::get(); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + + if (audioMixer && audioMixer->getActiveSocket()) { + Locker lock(_mutex); + if (!_audioPacket) { + // we don't have an audioPacket yet - set that up now + _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); + } + + glm::vec3 headPosition = _positionGetter(); + glm::quat headOrientation = _orientationGetter(); + quint8 isStereo = _isStereoInput ? 1 : 0; + + if (packetType == PacketType::Unknown) { if (_lastInputLoudness == 0) { _audioPacket->setType(PacketType::SilentAudioFrame); } else { @@ -866,70 +880,52 @@ void AudioClient::handleAudioInput() { _audioPacket->setType(PacketType::MicrophoneAudioNoEcho); } } - - // reset the audio packet so we can start writing - _audioPacket->reset(); - - // write sequence number - _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); - - if (_audioPacket->getType() == PacketType::SilentAudioFrame) { - // pack num silent samples - quint16 numSilentSamples = numNetworkSamples; - _audioPacket->writePrimitive(numSilentSamples); - } else { - // set the mono/stereo byte - _audioPacket->writePrimitive(isStereo); - } - - // pack the three float positions - _audioPacket->writePrimitive(headPosition); - - // pack the orientation - _audioPacket->writePrimitive(headOrientation); - - if (_audioPacket->getType() != PacketType::SilentAudioFrame) { - // audio samples have already been packed (written to networkAudioSamples) - _audioPacket->setPayloadSize(_audioPacket->getPayloadSize() + numNetworkBytes); - } - - _stats.sentPacket(); - - nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); - - nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); - - _outgoingAvatarAudioSequenceNumber++; + } else { + _audioPacket->setType(packetType); } + + // reset the audio packet so we can start writing + _audioPacket->reset(); + + // write sequence number + _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); + + if (_audioPacket->getType() == PacketType::SilentAudioFrame) { + // pack num silent samples + quint16 numSilentSamples = numNetworkSamples; + _audioPacket->writePrimitive(numSilentSamples); + } else { + // set the mono/stereo byte + _audioPacket->writePrimitive(isStereo); + } + + // pack the three float positions + _audioPacket->writePrimitive(headPosition); + + // pack the orientation + _audioPacket->writePrimitive(headOrientation); + + if (_audioPacket->getType() != PacketType::SilentAudioFrame) { + // audio samples have already been packed (written to networkAudioSamples) + _audioPacket->setPayloadSize(_audioPacket->getPayloadSize() + numNetworkBytes); + } + + static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8); + int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes); + memcpy(networkAudioSamples, audioData, numNetworkBytes); + + _stats.sentPacket(); + + nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); + + nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); + + _outgoingAvatarAudioSequenceNumber++; } } void AudioClient::handleRecordedAudioInput(const QByteArray& audio) { - if (!_audioPacket) { - // we don't have an audioPacket yet - set that up now - _audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho); - } - - // FIXME either discard stereo in the recording or record a stereo flag - - auto nodeList = DependencyManager::get(); - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - if (audioMixer && audioMixer->getActiveSocket()) { - glm::vec3 headPosition = _positionGetter(); - glm::quat headOrientation = _orientationGetter(); - quint8 isStereo = _isStereoInput ? 1 : 0; - _audioPacket->reset(); - _audioPacket->setType(PacketType::MicrophoneAudioWithEcho); - _audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber); - _audioPacket->writePrimitive(isStereo); - _audioPacket->writePrimitive(headPosition); - _audioPacket->writePrimitive(headOrientation); - _audioPacket->write(audio); - _stats.sentPacket(); - nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); - nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer); - _outgoingAvatarAudioSequenceNumber++; - } + emitAudioPacket((int16_t*)audio.data(), PacketType::MicrophoneAudioWithEcho); } void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 7d2b5a783f..9d46ad9d26 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -212,6 +212,7 @@ protected: } private: + void emitAudioPacket(const int16_t* audioData, PacketType packetType = PacketType::Unknown); void outputFormatChanged(); QByteArray firstInputFrame; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 017ef7578a..fdfc6c1893 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1443,14 +1443,10 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { auto recordingBasis = avatar.getRecordingBasis(); if (recordingBasis) { + root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); // Find the relative transform auto relativeTransform = recordingBasis->relativeTransform(avatar.getTransform()); - - // if the resulting relative basis is identity, we shouldn't record anything - if (!relativeTransform.isIdentity()) { - root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); - root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); - } + root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform); } else { root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatar.getTransform()); } @@ -1484,6 +1480,9 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) { void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) { QJsonDocument doc = QJsonDocument::fromBinaryData(frameData); +#ifdef WANT_JSON_DEBUG + qDebug() << doc.toJson(QJsonDocument::JsonFormat::Indented); +#endif QJsonObject root = doc.object(); if (root.contains(JSON_AVATAR_HEAD_MODEL)) { From 48b0465e56641daa06af5fbb92eeae69f5dbcec7 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 13:33:10 -0800 Subject: [PATCH 0981/1003] Fixing race condition on seek, correcting some issues with frame timing --- .../scripting/RecordingScriptingInterface.cpp | 28 +++++++++++------ .../scripting/RecordingScriptingInterface.h | 23 ++++++++------ libraries/recording/src/recording/Deck.cpp | 31 ++++++++++++++++--- 3 files changed, 59 insertions(+), 23 deletions(-) diff --git a/interface/src/scripting/RecordingScriptingInterface.cpp b/interface/src/scripting/RecordingScriptingInterface.cpp index 4ca3881e8d..d549de84b2 100644 --- a/interface/src/scripting/RecordingScriptingInterface.cpp +++ b/interface/src/scripting/RecordingScriptingInterface.cpp @@ -47,19 +47,19 @@ RecordingScriptingInterface::RecordingScriptingInterface() { connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput); } -bool RecordingScriptingInterface::isPlaying() { +bool RecordingScriptingInterface::isPlaying() const { return _player->isPlaying(); } -bool RecordingScriptingInterface::isPaused() { +bool RecordingScriptingInterface::isPaused() const { return _player->isPaused(); } -float RecordingScriptingInterface::playerElapsed() { +float RecordingScriptingInterface::playerElapsed() const { return _player->position(); } -float RecordingScriptingInterface::playerLength() { +float RecordingScriptingInterface::playerLength() const { return _player->length(); } @@ -103,6 +103,10 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) { } void RecordingScriptingInterface::setPlayerTime(float time) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setPlayerTime", Qt::BlockingQueuedConnection, Q_ARG(float, time)); + return; + } _player->seek(time); } @@ -130,23 +134,27 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode _useSkeletonModel = useSkeletonModel; } -void RecordingScriptingInterface::play() { - _player->play(); -} - void RecordingScriptingInterface::pausePlayer() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection); + return; + } _player->pause(); } void RecordingScriptingInterface::stopPlaying() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection); + return; + } _player->stop(); } -bool RecordingScriptingInterface::isRecording() { +bool RecordingScriptingInterface::isRecording() const { return _recorder->isRecording(); } -float RecordingScriptingInterface::recorderElapsed() { +float RecordingScriptingInterface::recorderElapsed() const { return _recorder->position(); } diff --git a/interface/src/scripting/RecordingScriptingInterface.h b/interface/src/scripting/RecordingScriptingInterface.h index 510a4b6898..27193e6f9a 100644 --- a/interface/src/scripting/RecordingScriptingInterface.h +++ b/interface/src/scripting/RecordingScriptingInterface.h @@ -25,12 +25,17 @@ public: RecordingScriptingInterface(); public slots: - bool isPlaying(); - bool isPaused(); - float playerElapsed(); - float playerLength(); void loadRecording(const QString& filename); + void startPlaying(); + void pausePlayer(); + void stopPlaying(); + bool isPlaying() const; + bool isPaused() const; + + float playerElapsed() const; + float playerLength() const; + void setPlayerVolume(float volume); void setPlayerAudioOffset(float audioOffset); void setPlayerTime(float time); @@ -40,13 +45,13 @@ public slots: void setPlayerUseAttachments(bool useAttachments); void setPlayerUseHeadModel(bool useHeadModel); void setPlayerUseSkeletonModel(bool useSkeletonModel); - void play(); - void pausePlayer(); - void stopPlaying(); - bool isRecording(); - float recorderElapsed(); + void startRecording(); void stopRecording(); + bool isRecording() const; + + float recorderElapsed() const; + void saveRecording(const QString& filename); void loadLastRecording(); diff --git a/libraries/recording/src/recording/Deck.cpp b/libraries/recording/src/recording/Deck.cpp index e52fcc16e6..6f624db191 100644 --- a/libraries/recording/src/recording/Deck.cpp +++ b/libraries/recording/src/recording/Deck.cpp @@ -8,6 +8,8 @@ #include "Deck.h" +#include + #include #include @@ -101,9 +103,13 @@ float Deck::position() const { } static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f); -static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.002f); +static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.004f); void Deck::processFrames() { + if (qApp->thread() != QThread::currentThread()) { + qWarning() << "Processing frames must only happen on the main thread."; + return; + } Locker lock(_mutex); if (_pause) { return; @@ -115,10 +121,17 @@ void Deck::processFrames() { // FIXME add code to start dropping frames if we fall behind. // Alternatively, add code to cache frames here and then process only the last frame of a given type // ... the latter will work for Avatar, but not well for audio I suspect. + bool overLimit = false; for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) { auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch); if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) { qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early"; +#ifdef WANT_RECORDING_DEBUG + qCDebug(recordingLog) << "Starting: " << currentPosition; + qCDebug(recordingLog) << "Current: " << startingPosition; + qCDebug(recordingLog) << "Trigger: " << triggerPosition; +#endif + overLimit = true; break; } @@ -150,9 +163,19 @@ void Deck::processFrames() { // If we have more clip frames available, set the timer for the next one _position = Frame::frameTimeFromEpoch(_startEpoch); - auto nextFrameTime = nextClip->positionFrameTime(); - auto interval = Frame::frameTimeToMilliseconds(nextFrameTime - _position); - _timer.singleShot(interval, [this] { + int nextInterval = 1; + if (!overLimit) { + auto nextFrameTime = nextClip->positionFrameTime(); + nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position); +#ifdef WANT_RECORDING_DEBUG + qCDebug(recordingLog) << "Now " << _position; + qCDebug(recordingLog) << "Next frame time " << nextInterval; +#endif + } +#ifdef WANT_RECORDING_DEBUG + qCDebug(recordingLog) << "Setting timer for next processing " << nextInterval; +#endif + _timer.singleShot(nextInterval, [this] { processFrames(); }); } From 3906a747b8b5515935e3c5b87a05bdc1d9f7a6e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 15:33:57 -0800 Subject: [PATCH 0982/1003] fix a couple of bad checks --- libraries/networking/src/LimitedNodeList.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 0d858ba3a3..0b9a04f039 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -378,7 +378,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, const Node& destinationNode) { auto activeSocket = destinationNode.getActiveSocket(); - if (!activeSocket) { + if (activeSocket) { // close the last packet in the list packetList->closeCurrentPacket(); @@ -397,7 +397,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr) { - if (!overridenSockAddr.isNull() && !destinationNode.getActiveSocket()) { + if (overridenSockAddr.isNull() && !destinationNode.getActiveSocket()) { qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node. Not sending."; return 0; } From f5ec458a5eb0cae74d78b78df5d5fca64a710149 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 15:38:03 -0800 Subject: [PATCH 0983/1003] make activeSocket checks more consistent --- libraries/networking/src/LimitedNodeList.cpp | 42 ++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 0b9a04f039..333cdb99f0 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -300,15 +300,16 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiS qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& destinationNode) { Q_ASSERT(!packet->isPartOfMessage()); auto activeSocket = destinationNode.getActiveSocket(); - if (!activeSocket) { + + if (activeSocket) { + emit dataSent(destinationNode.getType(), packet->getDataSize()); + destinationNode.recordBytesSent(packet->getDataSize()); + + return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret()); + } else { qDebug() << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending"; return 0; } - - emit dataSent(destinationNode.getType(), packet->getDataSize()); - destinationNode.recordBytesSent(packet->getDataSize()); - - return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret()); } qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, @@ -329,24 +330,25 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiS qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const Node& destinationNode) { auto activeSocket = destinationNode.getActiveSocket(); - if (!activeSocket) { + + if (activeSocket) { + qint64 bytesSent = 0; + auto connectionSecret = destinationNode.getConnectionSecret(); + + // close the last packet in the list + packetList.closeCurrentPacket(); + + while (!packetList._packets.empty()) { + bytesSent += sendPacket(packetList.takeFront(), *activeSocket, connectionSecret); + } + + emit dataSent(destinationNode.getType(), bytesSent); + return bytesSent; + } else { qDebug() << "LimitedNodeList::sendPacketList called without active socket for node" << destinationNode << " - not sending."; return 0; } - - qint64 bytesSent = 0; - auto connectionSecret = destinationNode.getConnectionSecret(); - - // close the last packet in the list - packetList.closeCurrentPacket(); - - while (!packetList._packets.empty()) { - bytesSent += sendPacket(packetList.takeFront(), *activeSocket, connectionSecret); - } - - emit dataSent(destinationNode.getType(), bytesSent); - return bytesSent; } qint64 LimitedNodeList::sendPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr, From 073215d067134f360b43f5d3c453554f5ce4b458 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 15:55:41 -0800 Subject: [PATCH 0984/1003] handle socketActivated --- libraries/networking/src/MessagesClient.cpp | 19 ++++++++++++++----- libraries/networking/src/MessagesClient.h | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index 51cfba263c..f17ea4555a 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -84,7 +84,6 @@ void MessagesClient::subscribe(const QString& channel) { auto packetList = NLPacketList::create(PacketType::MessagesSubscribe, QByteArray(), true, true); packetList->write(channel.toUtf8()); nodeList->sendPacketList(std::move(packetList), *messagesMixer); - qDebug() << "sending MessagesSubscribe for channel:" << channel; } } @@ -102,10 +101,20 @@ void MessagesClient::unsubscribe(const QString& channel) { void MessagesClient::handleNodeAdded(SharedNodePointer node) { if (node->getType() == NodeType::MessagesMixer) { - qDebug() << "messages-mixer node type added..."; - for (const auto& channel : _subscribedChannels) { - qDebug() << "subscribing to channel:" << channel; - subscribe(channel); + if (!node->getActiveSocket()) { + connect(node.data(), &NetworkPeer::socketActivated, this, &MessagesClient::socketActivated); + } else { + resubscribeToAll(); } } } + +void MessagesClient::socketActivated(const HifiSockAddr& sockAddr) { + resubscribeToAll(); +} + +void MessagesClient::resubscribeToAll() { + for (const auto& channel : _subscribedChannels) { + subscribe(channel); + } +} diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index 695e7e789e..b5f590bc0d 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -38,8 +38,10 @@ signals: private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); void handleNodeAdded(SharedNodePointer node); + void socketActivated(const HifiSockAddr& sockAddr); protected: + void resubscribeToAll(); QSet _subscribedChannels; }; From 293914b84f16b0e12c7495e10d532a22ff5f5704 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 17 Nov 2015 16:31:34 -0800 Subject: [PATCH 0985/1003] added NodeActivated signal to make it easier for users to know when a recently added node has an active socket --- libraries/networking/src/LimitedNodeList.cpp | 10 +++++++++ libraries/networking/src/LimitedNodeList.h | 1 + libraries/networking/src/MessagesClient.cpp | 22 +++++--------------- libraries/networking/src/MessagesClient.h | 4 +--- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 333cdb99f0..4a7844ecc7 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -530,11 +530,21 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t qCDebug(networking) << "Added" << *newNode; emit nodeAdded(newNodePointer); + if (newNodePointer->getActiveSocket()) { + emit nodeActivated(newNodePointer); + } else { + connect(newNodePointer.data(), &NetworkPeer::socketActivated, this, [=] { + emit nodeActivated(newNodePointer); + disconnect(newNodePointer.data(), &NetworkPeer::socketActivated, this, 0); + }); + } return newNodePointer; } } + + std::unique_ptr LimitedNodeList::constructPingPacket(PingType_t pingType) { int packetSize = sizeof(PingType_t) + sizeof(quint64); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 1aacd27572..26e648421a 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -239,6 +239,7 @@ signals: void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID); void nodeAdded(SharedNodePointer); void nodeKilled(SharedNodePointer); + void nodeActivated(SharedNodePointer); void localSockAddrChanged(const HifiSockAddr& localSockAddr); void publicSockAddrChanged(const HifiSockAddr& publicSockAddr); diff --git a/libraries/networking/src/MessagesClient.cpp b/libraries/networking/src/MessagesClient.cpp index f17ea4555a..2e8b3bbb09 100644 --- a/libraries/networking/src/MessagesClient.cpp +++ b/libraries/networking/src/MessagesClient.cpp @@ -27,7 +27,7 @@ MessagesClient::MessagesClient() { auto nodeList = DependencyManager::get(); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerMessageListener(PacketType::MessagesData, this, "handleMessagesPacket"); - connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &MessagesClient::handleNodeAdded); + connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &MessagesClient::handleNodeActivated); } void MessagesClient::init() { @@ -99,22 +99,10 @@ void MessagesClient::unsubscribe(const QString& channel) { } } -void MessagesClient::handleNodeAdded(SharedNodePointer node) { +void MessagesClient::handleNodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::MessagesMixer) { - if (!node->getActiveSocket()) { - connect(node.data(), &NetworkPeer::socketActivated, this, &MessagesClient::socketActivated); - } else { - resubscribeToAll(); + for (const auto& channel : _subscribedChannels) { + subscribe(channel); } } -} - -void MessagesClient::socketActivated(const HifiSockAddr& sockAddr) { - resubscribeToAll(); -} - -void MessagesClient::resubscribeToAll() { - for (const auto& channel : _subscribedChannels) { - subscribe(channel); - } -} +} \ No newline at end of file diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h index b5f590bc0d..a1ae4cb5ba 100644 --- a/libraries/networking/src/MessagesClient.h +++ b/libraries/networking/src/MessagesClient.h @@ -37,11 +37,9 @@ signals: private slots: void handleMessagesPacket(QSharedPointer packetList, SharedNodePointer senderNode); - void handleNodeAdded(SharedNodePointer node); - void socketActivated(const HifiSockAddr& sockAddr); + void handleNodeActivated(SharedNodePointer node); protected: - void resubscribeToAll(); QSet _subscribedChannels; }; From 0ad1d080411fb0f305ac098056c3eb25bd978d89 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 16:46:25 -0800 Subject: [PATCH 0986/1003] Exclude avatar scales out of the permissable range --- libraries/avatars/src/AvatarData.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 017ef7578a..9f3cbf092b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -178,7 +178,7 @@ float AvatarData::getTargetScale() const { void AvatarData::setTargetScale(float targetScale, bool overideReferential) { if (!_referential || overideReferential) { - _targetScale = targetScale; + _targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale)); } } @@ -532,7 +532,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { } return maxAvailableSize; } - _targetScale = scale; + _targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, scale)); } // 20 bytes { // Lookat Position From e0a9048287e371f4455ec11c5a0653a9bff95713 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Tue, 17 Nov 2015 16:59:14 -0800 Subject: [PATCH 0987/1003] new bubble model --- examples/toybox/bubblewand/wand.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toybox/bubblewand/wand.js b/examples/toybox/bubblewand/wand.js index 4bdc789612..8036d9ead6 100644 --- a/examples/toybox/bubblewand/wand.js +++ b/examples/toybox/bubblewand/wand.js @@ -16,7 +16,7 @@ Script.include("../../libraries/utils.js"); - var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx"; + var BUBBLE_MODEL = "http://hifi-content.s3.amazonaws.com/james/bubblewand/bubble.fbx"; var BUBBLE_INITIAL_DIMENSIONS = { x: 0.01, From 4e57c9114cc1cc4a09774e532743672a5560f54e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 17:13:53 -0800 Subject: [PATCH 0988/1003] Avatar has no dependency on audio or recording anymore --- libraries/avatars/CMakeLists.txt | 2 +- libraries/avatars/src/AvatarData.cpp | 1 - libraries/avatars/src/AvatarData.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/avatars/CMakeLists.txt b/libraries/avatars/CMakeLists.txt index 6d4d9cc341..fc6d15cced 100644 --- a/libraries/avatars/CMakeLists.txt +++ b/libraries/avatars/CMakeLists.txt @@ -1,3 +1,3 @@ set(TARGET_NAME avatars) setup_hifi_library(Network Script) -link_hifi_libraries(audio shared networking recording) +link_hifi_libraries(shared networking) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index fdfc6c1893..cc4139184d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include "AvatarLogging.h" diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e79c0be80a..846c314e4b 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -50,7 +50,6 @@ typedef unsigned long long quint64; #include #include #include -#include #include "AABox.h" #include "HandData.h" From 94f18672d42ac8db2e5617605ffd85d1e62d3432 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 Nov 2015 17:18:16 -0800 Subject: [PATCH 0989/1003] equip from a distance uses a spring to pull the object to the hand before equipping it --- examples/controllers/handControllerGrab.js | 92 ++++++++++++++++++++-- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index efd288c751..dd3a9a4b96 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -55,6 +55,13 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed +// +// equip +// + +var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05; +var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position + // // other constants // @@ -70,7 +77,7 @@ var ZERO_VEC = { var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}"; var MSEC_PER_SEC = 1000.0; -// these control how long an abandoned pointer line will hang around +// these control how long an abandoned pointer line or action will hang around var LIFETIME = 10; var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; @@ -113,6 +120,7 @@ var STATE_EQUIP = 12 var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down var STATE_CONTINUE_EQUIP = 14; var STATE_WAITING_FOR_BUMPER_RELEASE = 15; +var STATE_EQUIP_SPRING = 16; function stateToName(state) { @@ -149,6 +157,8 @@ function stateToName(state) { return "continue_equip"; case STATE_WAITING_FOR_BUMPER_RELEASE: return "waiting_for_bumper_release"; + case STATE_EQUIP_SPRING: + return "state_equip_spring"; } return "unknown"; @@ -228,14 +238,15 @@ function MyController(hand) { this.continueDistanceHolding(); break; case STATE_NEAR_GRABBING: - this.nearGrabbing(); - break; case STATE_EQUIP: this.nearGrabbing(); break; case STATE_WAITING_FOR_BUMPER_RELEASE: this.waitingForBumperRelease(); break; + case STATE_EQUIP_SPRING: + this.pullTowardEquipPosition() + break; case STATE_CONTINUE_NEAR_GRABBING: case STATE_CONTINUE_EQUIP_BD: case STATE_CONTINUE_EQUIP: @@ -444,7 +455,15 @@ function MyController(hand) { return; } else if (!intersection.properties.locked) { this.grabbedEntity = intersection.entityID; - this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP) + if (this.state == STATE_SEARCHING) { + this.setState(STATE_NEAR_GRABBING); + } else { // equipping + if (typeof grabbableData.spatialKey !== 'undefined') { + this.setState(STATE_EQUIP_SPRING); + } else { + this.setState(STATE_EQUIP); + } + } return; } } else if (! entityIsGrabbedByOther(intersection.entityID)) { @@ -455,7 +474,7 @@ function MyController(hand) { this.grabbedEntity = intersection.entityID; if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) { // if a distance pick in equip mode hits something with a spatialKey, equip it - this.setState(STATE_EQUIP); + this.setState(STATE_EQUIP_SPRING); return; } else if (this.state == STATE_SEARCHING) { this.setState(STATE_DISTANCE_HOLDING); @@ -750,7 +769,13 @@ function MyController(hand) { this.actionID = null; } else { this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); - this.setState(this.state == STATE_NEAR_GRABBING ? STATE_CONTINUE_NEAR_GRABBING : STATE_CONTINUE_EQUIP_BD) + if (this.state == STATE_NEAR_GRABBING) { + this.setState(STATE_CONTINUE_NEAR_GRABBING); + } else { + // equipping + this.setState(STATE_CONTINUE_EQUIP_BD); + } + if (this.hand === RIGHT_HAND) { Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); } else { @@ -822,7 +847,60 @@ function MyController(hand) { this.setState(STATE_RELEASE); Entities.callEntityMethod(this.grabbedEntity, "releaseGrab"); } - } + }; + + this.pullTowardEquipPosition = function() { + this.lineOff(); + + var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + + // use a spring to pull the object to where it will be when equipped + var relativeRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 }; + var relativePosition = { x: 0.0, y: 0.0, z: 0.0 }; + if (grabbableData.spatialKey.relativePosition) { + relativePosition = grabbableData.spatialKey.relativePosition; + } + if (grabbableData.spatialKey.relativeRotation) { + relativeRotation = grabbableData.spatialKey.relativeRotation; + } + var handRotation = this.getHandRotation(); + var handPosition = this.getHandPosition(); + var targetRotation = Quat.multiply(handRotation, relativeRotation); + var offset = Vec3.multiplyQbyV(targetRotation, relativePosition); + var targetPosition = Vec3.sum(handPosition, offset); + + if (typeof this.equipSpringID === 'undefined' || + this.equipSpringID === null || + this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL + }); + if (this.equipSpringID === NULL_ACTION_ID) { + this.equipSpringID = null; + this.setState(STATE_OFF); + return; + } + } else { + Entities.updateAction(this.grabbedEntity, this.equipSpringID, { + targetPosition: targetPosition, + linearTimeScale: EQUIP_SPRING_TIMEFRAME, + targetRotation: targetRotation, + angularTimeScale: EQUIP_SPRING_TIMEFRAME, + ttl: ACTION_TTL + }); + } + + if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) { + Entities.deleteAction(this.grabbedEntity, this.equipSpringID); + this.equipSpringID = null; + this.setState(STATE_EQUIP); + } + }; this.nearTrigger = function() { if (this.triggerSmoothedReleased()) { From 8566d84709c7535472c4dda164a9e89a25fb4e7d Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 17 Nov 2015 10:51:34 -0800 Subject: [PATCH 0990/1003] fixed cleanup - unload --- examples/entityScripts/recordingEntityScript.js | 2 +- examples/entityScripts/recordingMaster.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/entityScripts/recordingEntityScript.js b/examples/entityScripts/recordingEntityScript.js index ede6f4fbe2..6f41d20e0e 100644 --- a/examples/entityScripts/recordingEntityScript.js +++ b/examples/entityScripts/recordingEntityScript.js @@ -80,7 +80,7 @@ } } }, - clean: function(entityID) { + unload: function(entityID) { Script.update.disconnect(_this.update); } } diff --git a/examples/entityScripts/recordingMaster.js b/examples/entityScripts/recordingMaster.js index 3cec521ce0..718d47eb92 100644 --- a/examples/entityScripts/recordingMaster.js +++ b/examples/entityScripts/recordingMaster.js @@ -106,7 +106,7 @@ function mousePressEvent(event) { function cleanup() { toolBar.cleanup(); - Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings + //Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings Entities.deleteEntity(recordAreaEntity); } From 5400381583df5061c9dc1334752ee8511be2d59d Mon Sep 17 00:00:00 2001 From: AlessandroSigna Date: Tue, 17 Nov 2015 18:01:51 -0800 Subject: [PATCH 0991/1003] improved the structure --- examples/entityScripts/createRecorder.js | 21 +++++ .../entityScripts/recordingEntityScript.js | 81 +++++++++++-------- examples/entityScripts/recordingMaster.js | 54 +++++-------- 3 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 examples/entityScripts/createRecorder.js diff --git a/examples/entityScripts/createRecorder.js b/examples/entityScripts/createRecorder.js new file mode 100644 index 0000000000..7f89898ceb --- /dev/null +++ b/examples/entityScripts/createRecorder.js @@ -0,0 +1,21 @@ +var rotation = Quat.safeEulerAngles(Camera.getOrientation()); +rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0); +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(6, Quat.getFront(rotation))); + +var recordAreaEntity = Entities.addEntity({ + name: 'recorderEntity', + dimensions: { + x: 10, + y: 10, + z: 10 + }, + type: 'Box', + position: center, + color: { + red: 255, + green: 255, + blue: 255 + }, + visible: true, + script: "https://hifi-public.s3.amazonaws.com/sam/record/recordingEntityScript.js", +}); \ No newline at end of file diff --git a/examples/entityScripts/recordingEntityScript.js b/examples/entityScripts/recordingEntityScript.js index 6f41d20e0e..1b74466c4c 100644 --- a/examples/entityScripts/recordingEntityScript.js +++ b/examples/entityScripts/recordingEntityScript.js @@ -13,9 +13,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +(function () { + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js"); -(function() { var insideRecorderArea = false; var enteredInTime = false; var isAvatarRecording = false; @@ -25,51 +27,63 @@ _this = this; return; } - + + function update() { + var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted; + if (isRecordingStarted && !isAvatarRecording) { + _this.startRecording(); + } else if ((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)) { + _this.stopRecording(); + } else if (!isRecordingStarted && insideRecorderArea && !enteredInTime) { + //if an avatar enters the zone while a recording is started he will be able to participate to the next group recording + enteredInTime = true; + } + }; + recordingEntity.prototype = { - update: function(){ - var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); - var isRecordingStarted = userData.recordingKey.isRecordingStarted; - if(isRecordingStarted && !isAvatarRecording){ - _this.startRecording(); - }else if((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)){ - _this.stopRecording(); - }else if(!isRecordingStarted && insideRecorderArea && !enteredInTime){ - //if an avatar enters the zone while a recording is started he will be able to participate to the next group recording - enteredInTime = true; - } - - }, - preload: function(entityID) { + + preload: function (entityID) { + print("RECORDING ENTITY PRELOAD"); this.entityID = entityID; - Script.update.connect(_this.update); + + var entityProperties = Entities.getEntityProperties(_this.entityID); + if (!entityProperties.ignoreForCollisions) { + Entities.editEntity(_this.entityID, { ignoreForCollisions: true }); + } + + //print(JSON.stringify(entityProperties)); + var recordingKey = getEntityCustomData("recordingKey", _this.entityID, undefined); + if (recordingKey === undefined) { + setEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }); + } + + Script.update.connect(update); }, - enterEntity: function(entityID) { + enterEntity: function (entityID) { print("entering in the recording area"); insideRecorderArea = true; - var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData); - var isRecordingStarted = userData.recordingKey.isRecordingStarted; - if(!isRecordingStarted){ + var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted; + if (!isRecordingStarted) { //i'm in the recording area in time (before the event starts) enteredInTime = true; } }, - leaveEntity: function(entityID) { + leaveEntity: function (entityID) { print("leaving the recording area"); insideRecorderArea = false; enteredInTime = false; }, - - startRecording: function(entityID){ - if(enteredInTime && !isAvatarRecording){ + + startRecording: function (entityID) { + if (enteredInTime && !isAvatarRecording) { print("RECORDING STARTED"); Recording.startRecording(); isAvatarRecording = true; } }, - - stopRecording: function(entityID){ - if(isAvatarRecording){ + + stopRecording: function (entityID) { + if (isAvatarRecording) { print("RECORDING ENDED"); Recording.stopRecording(); Recording.loadLastRecording(); @@ -80,12 +94,13 @@ } } }, - unload: function(entityID) { - Script.update.disconnect(_this.update); + unload: function (entityID) { + print("RECORDING ENTITY UNLOAD"); + Script.update.disconnect(update); } } - - + + return new recordingEntity(); -}); +}); \ No newline at end of file diff --git a/examples/entityScripts/recordingMaster.js b/examples/entityScripts/recordingMaster.js index 718d47eb92..71a92a05f3 100644 --- a/examples/entityScripts/recordingMaster.js +++ b/examples/entityScripts/recordingMaster.js @@ -5,16 +5,15 @@ // Created by Alessandro Signa on 11/12/15. // Copyright 2015 High Fidelity, Inc. // -// Run this script to spawn a box (recorder) and drive the start/end of the recording for anyone who is inside the box +// Run this script to find the recorder (created by crateRecorder.js) and drive the start/end of the recording for anyone who is inside the box // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -var PARAMS_SCRIPT_URL = Script.resolvePath('recordingEntityScript.js'); HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -Script.include("../libraries/toolBars.js"); -Script.include("../libraries/utils.js"); +Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/toolBars.js"); +Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js"); @@ -30,35 +29,25 @@ var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 }; var toolBar = null; var recordIcon; - +var isRecordingEntityFound = false; var isRecording = false; -var recordAreaEntity = Entities.addEntity({ - name: 'recorderEntity', - dimensions: { - x: 2, - y: 1, - z: 2 - }, - type: 'Box', - position: center, - color: { - red: 255, - green: 255, - blue: 255 - }, - visible: true, - ignoreForCollisions: true, - script: PARAMS_SCRIPT_URL, - - userData: JSON.stringify({ - recordingKey: { - isRecordingStarted: false - } - }) -}); +var recordAreaEntity = null; +findRecorder(); +function findRecorder() { + foundEntities = Entities.findEntities(MyAvatar.position, 50); + for (var i = 0; i < foundEntities.length; i++) { + var name = Entities.getEntityProperties(foundEntities[i], "name").name; + if (name === "recorderEntity") { + recordAreaEntity = foundEntities[i]; + isRecordingEntityFound = true; + print("Found recorder Entity!"); + return; + } + } +} setupToolBar(); @@ -70,7 +59,7 @@ function setupToolBar() { Tool.IMAGE_HEIGHT /= 2; Tool.IMAGE_WIDTH /= 2; - toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); //put the button in the up-left corner + toolBar = new ToolBar(0, 100, ToolBar.HORIZONTAL); //put the button in the up-left corner toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF); @@ -81,9 +70,8 @@ function setupToolBar() { width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT, alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON, - visible: true + visible: isRecordingEntityFound, }, true, isRecording); - } function mousePressEvent(event) { @@ -106,8 +94,6 @@ function mousePressEvent(event) { function cleanup() { toolBar.cleanup(); - //Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings - Entities.deleteEntity(recordAreaEntity); } From 68c6718d1ca93410594fb8ae5ffc7e1d6edfd7ad Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 18:20:55 -0800 Subject: [PATCH 0992/1003] Fixes for windows 64 bit builds --- cmake/externals/openvr/CMakeLists.txt | 11 ++++++++--- cmake/externals/sdl2/CMakeLists.txt | 11 +++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cmake/externals/openvr/CMakeLists.txt b/cmake/externals/openvr/CMakeLists.txt index dea59f41a0..f9d0ef5a71 100644 --- a/cmake/externals/openvr/CMakeLists.txt +++ b/cmake/externals/openvr/CMakeLists.txt @@ -25,9 +25,14 @@ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNA if (WIN32) - # FIXME need to account for different architectures - set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL) - add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) + # FIXME need to account for different architectures + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win64/openvr_api.lib CACHE TYPE INTERNAL) + add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win64) + else() + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL) + add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) + endif() elseif(APPLE) diff --git a/cmake/externals/sdl2/CMakeLists.txt b/cmake/externals/sdl2/CMakeLists.txt index decd1c6906..ee1d57d2e9 100644 --- a/cmake/externals/sdl2/CMakeLists.txt +++ b/cmake/externals/sdl2/CMakeLists.txt @@ -66,8 +66,15 @@ if (APPLE) elseif (WIN32) ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") - set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x64/SDL2.lib CACHE FILEPATH "Path to SDL2 library") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x64 CACHE PATH "Location of SDL2 DLL") + else() + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") + endif() + else () ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") From c21fbc9a469ca0c6903b097f0fec53c7b94bd5d8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 18:53:23 -0800 Subject: [PATCH 0993/1003] fix for messages-mixer bail early on empty settings --- assignment-client/src/AssignmentClient.cpp | 12 +++--- assignment-client/src/AssignmentClient.h | 1 + .../src/messages/MessagesMixer.cpp | 43 ++++++++++--------- assignment-client/src/octree/OctreeServer.cpp | 11 +++-- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index bf5f9c3b7f..2d11f4d289 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -198,7 +198,7 @@ void AssignmentClient::sendStatusPacketToACM() { } void AssignmentClient::sendAssignmentRequest() { - if (!_currentAssignment) { + if (!_currentAssignment && !_isAssigned) { auto nodeList = DependencyManager::get(); @@ -229,8 +229,9 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer pac // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(*packet); - if (_currentAssignment) { + if (_currentAssignment && !_isAssigned) { qDebug() << "Received an assignment -" << *_currentAssignment; + _isAssigned = true; auto nodeList = DependencyManager::get(); @@ -309,12 +310,11 @@ void AssignmentClient::handleAuthenticationRequest() { } void AssignmentClient::assignmentCompleted() { - // we expect that to be here the previous assignment has completely cleaned up assert(_currentAssignment.isNull()); - // reset our current assignment pointer to NULL now that it has been deleted - _currentAssignment = NULL; + // reset our current assignment pointer to null now that it has been deleted + _currentAssignment = nullptr; // reset the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); @@ -330,4 +330,6 @@ void AssignmentClient::assignmentCompleted() { nodeList->setOwnerType(NodeType::Unassigned); nodeList->reset(); nodeList->resetNodeInterestSet(); + + _isAssigned = false; } diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index 9d2c816861..9d7591f931 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -46,6 +46,7 @@ private: Assignment _requestAssignment; QPointer _currentAssignment; + bool _isAssigned { false }; QString _assignmentServerHostname; HifiSockAddr _assignmentServerSocket; QTimer _requestTimer; // timer for requesting and assignment diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index 99798b2d4f..f91076c335 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -128,27 +128,30 @@ void MessagesMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - + + // The messages-mixer currently does not have any settings, so it would be kind of insane to bail on an empty settings + // object. The below can be uncommented once messages-mixer settings are enabled. + // wait until we have the domain-server settings, otherwise we bail - DomainHandler& domainHandler = nodeList->getDomainHandler(); - - qDebug() << "Waiting for domain settings from domain-server."; - - // block until we get the settingsRequestComplete signal - QEventLoop loop; - connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); - connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); - domainHandler.requestDomainSettings(); - loop.exec(); - - if (domainHandler.getSettingsObject().isEmpty()) { - qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; - setFinished(true); - return; - } - - // parse the settings to pull out the values we need - parseDomainServerSettings(domainHandler.getSettingsObject()); +// DomainHandler& domainHandler = nodeList->getDomainHandler(); +// +// qDebug() << "Waiting for domain settings from domain-server."; +// +// // block until we get the settingsRequestComplete signal +// QEventLoop loop; +// connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); +// connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); +// domainHandler.requestDomainSettings(); +// loop.exec(); +// +// if (domainHandler.getSettingsObject().isEmpty()) { +// qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; +// setFinished(true); +// return; +// } +// +// // parse the settings to pull out the values we need +// parseDomainServerSettings(domainHandler.getSettingsObject()); } void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index ad3df11474..84749bd975 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -953,7 +953,6 @@ bool OctreeServer::readConfiguration() { if (domainHandler.getSettingsObject().isEmpty()) { qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; - setFinished(true); return false; } @@ -1086,12 +1085,16 @@ void OctreeServer::run() { auto nodeList = DependencyManager::get(); nodeList->setOwnerType(getMyNodeType()); - // use common init to setup common timers and logging commonInit(getMyLoggingServerTargetName(), getMyNodeType()); + + // we need to ask the DS about agents so we can ping/reply with them + nodeList->addNodeTypeToInterestSet(NodeType::Agent); // read the configuration from either the payload or the domain server configuration if (!readConfiguration()) { + qDebug() << "OctreeServer bailing on run since readConfiguration has failed."; + setFinished(true); return; // bailing on run, because readConfiguration failed } @@ -1100,10 +1103,6 @@ void OctreeServer::run() { connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer))); - - // we need to ask the DS about agents so we can ping/reply with them - nodeList->addNodeTypeToInterestSet(NodeType::Agent); - #ifndef WIN32 setvbuf(stdout, NULL, _IOLBF, 0); #endif From 2355ba70caa81e460599651cfcc3116692d82e28 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 18:55:09 -0800 Subject: [PATCH 0994/1003] just remove messages-mixer settings grabbing all together --- .../src/messages/MessagesMixer.cpp | 30 ++----------------- .../src/messages/MessagesMixer.h | 2 -- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index f91076c335..d3662f3fb5 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -129,32 +129,6 @@ void MessagesMixer::run() { auto nodeList = DependencyManager::get(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - // The messages-mixer currently does not have any settings, so it would be kind of insane to bail on an empty settings - // object. The below can be uncommented once messages-mixer settings are enabled. - - // wait until we have the domain-server settings, otherwise we bail -// DomainHandler& domainHandler = nodeList->getDomainHandler(); -// -// qDebug() << "Waiting for domain settings from domain-server."; -// -// // block until we get the settingsRequestComplete signal -// QEventLoop loop; -// connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit); -// connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit); -// domainHandler.requestDomainSettings(); -// loop.exec(); -// -// if (domainHandler.getSettingsObject().isEmpty()) { -// qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment."; -// setFinished(true); -// return; -// } -// -// // parse the settings to pull out the values we need -// parseDomainServerSettings(domainHandler.getSettingsObject()); -} - -void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { - // TODO - if we want options, parse them here... - const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer"; + // The messages-mixer currently does currently have any domain settings. If it did, they would be + // synchronously grabbed here. } diff --git a/assignment-client/src/messages/MessagesMixer.h b/assignment-client/src/messages/MessagesMixer.h index 12667bcc1b..65419a8ca6 100644 --- a/assignment-client/src/messages/MessagesMixer.h +++ b/assignment-client/src/messages/MessagesMixer.h @@ -35,8 +35,6 @@ private slots: void handleMessagesUnsubscribe(QSharedPointer packetList, SharedNodePointer senderNode); private: - void parseDomainServerSettings(const QJsonObject& domainSettings); - QHash> _channelSubscribers; }; From c1675bba2168b610e389fc7dc01409a01ba12001 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 17 Nov 2015 19:01:29 -0800 Subject: [PATCH 0995/1003] Fix typo introduced by debug --- examples/acScripts/ControlACs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/acScripts/ControlACs.js b/examples/acScripts/ControlACs.js index e3d63cc26e..ba066d9750 100644 --- a/examples/acScripts/ControlACs.js +++ b/examples/acScripts/ControlACs.js @@ -156,7 +156,7 @@ function sendCommand(id, action) { position: controlEntityPosition, dimensions: { x: controlEntitySize, y: controlEntitySize, z: controlEntitySize }, visible: false, - lifetime: 10000, + lifetime: 10, userData: JSON.stringify({ idKey: { uD_id: id From ec918a1cf5b16153eb1c5bf30c8cbcaf3a443861 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 17 Nov 2015 19:10:16 -0800 Subject: [PATCH 0996/1003] Fix OpenSSL 64 bit search logic --- cmake/modules/FindOpenSSL.cmake | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index db3b2ba477..2142322687 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -34,17 +34,26 @@ if (UNIX) endif () if (WIN32) - # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ) + file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL/" "C:/OpenSSL-Win32/" "C:/OpenSSL-Win64/" - ) + + if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + # http://www.slproweb.com/products/Win32OpenSSL.html + set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" + ) + set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win64" "C:/OpenSSL/" "C:/OpenSSL-Win64/") + else() + # http://www.slproweb.com/products/Win32OpenSSL.html + set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" + ) + set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "C:/OpenSSL/" "C:/OpenSSL-Win32/") + endif() + unset(_programfiles) set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS}) + else () include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("openssl") From ee751ed1b9da0aefda68cb7018303d601b4896bc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 09:21:04 -0800 Subject: [PATCH 0997/1003] use kDop volume of avatar mesh parts for capsule --- interface/src/avatar/SkeletonModel.cpp | 16 ++---- libraries/fbx/src/FBXReader.cpp | 79 +++++++++++--------------- libraries/fbx/src/FBXReader.h | 1 - 3 files changed, 38 insertions(+), 58 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 87f0e631f2..c6c1ead8c1 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -625,19 +625,15 @@ void SkeletonModel::computeBoundingShape() { totalExtents.addPoint(glm::vec3(0.0f)); int numStates = _rig->getJointStateCount(); for (int i = 0; i < numStates; i++) { - const JointState& state = _rig->getJointState(i); - - const glm::mat4& jointTransform = state.getTransform(); - float scale = extractUniformScale(jointTransform); - // Each joint contributes a capsule defined by FBXJoint.shapeInfo. // For totalExtents we use the capsule endpoints expanded by the radius. + const JointState& state = _rig->getJointState(i); + const glm::mat4& jointTransform = state.getTransform(); const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo; - for (int j = 0; j < shapeInfo.points.size(); ++j) { - glm::vec3 transformedPoint = extractTranslation(jointTransform * glm::translate(shapeInfo.points[j])); - vec3 radius(scale * shapeInfo.radius); - totalExtents.addPoint(transformedPoint + radius); - totalExtents.addPoint(transformedPoint - radius); + if (shapeInfo.points.size() > 0) { + for (int j = 0; j < shapeInfo.points.size(); ++j) { + totalExtents.addPoint(extractTranslation(jointTransform * glm::translate(shapeInfo.points[j]))); + } } // HACK so that default legless robot doesn't knuckle-drag if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index f9bb089a9c..3251b5b35d 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1460,7 +1460,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS it != extracted.newIndices.end() && it.key() == oldIndex; it++) { // remember vertices with at least 1/4 weight - const float EXPANSION_WEIGHT_THRESHOLD = 0.25f; + const float EXPANSION_WEIGHT_THRESHOLD = 0.99f; if (weight > EXPANSION_WEIGHT_THRESHOLD) { // transform to joint-frame and save for later const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value())); @@ -1535,63 +1535,48 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS meshIDsToMeshIndices.insert(it.key(), meshIndex); } - // now that all joints have been scanned, compute a radius for each bone + ShapeVertices cardinalDirections; + cardinalDirections.push_back(Vectors::UNIT_X); + cardinalDirections.push_back(Vectors::UNIT_Y); + cardinalDirections.push_back(Vectors::UNIT_Z); + const float INV_SQRT_3 = 0.57735026918f; + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3)); + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3)); + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3)); + cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)); + + // now that all joints have been scanned compute a k-Dop bounding volume of mesh glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); for (int i = 0; i < geometry.joints.size(); ++i) { FBXJoint& joint = geometry.joints[i]; // NOTE: points are in joint-frame - // compute average point ShapeVertices& points = shapeVertices[i]; - glm::vec3 avgPoint = glm::vec3(0.0f); - for (uint32_t j = 0; j < points.size(); ++j) { - avgPoint += points[j]; - } - avgPoint /= (float)points.size(); - - // compute axis from begin to avgPoint - glm::vec3 begin(0.0f); - glm::vec3 end = avgPoint; - glm::vec3 axis = end - begin; - float axisLength = glm::length(axis); - if (axisLength > EPSILON) { - axis /= axisLength; - } else { - axis = glm::vec3(0.0f); - } - - // measure average cylindrical radius - float avgRadius = 0.0f; if (points.size() > 0) { - float minProjection = FLT_MAX; - float maxProjection = -FLT_MIN; + // compute average point + glm::vec3 avgPoint = glm::vec3(0.0f); for (uint32_t j = 0; j < points.size(); ++j) { - glm::vec3 offset = points[j] - avgPoint; - float projection = glm::dot(offset, axis); - maxProjection = glm::max(maxProjection, projection); - minProjection = glm::min(minProjection, projection); - avgRadius += glm::length(offset - projection * axis); + avgPoint += points[j]; } - avgRadius /= (float)points.size(); - - // compute endpoints of capsule in joint-frame - glm::vec3 capsuleBegin = avgPoint; - glm::vec3 capsuleEnd = avgPoint; - if (maxProjection - minProjection < 2.0f * avgRadius) { - // the mesh-as-cylinder approximation is too short to collide as a capsule - // so we'll collapse it to a sphere (although that isn't a very good approximation) - capsuleBegin = avgPoint + 0.5f * (maxProjection + minProjection) * axis; - capsuleEnd = capsuleBegin; - } else { - capsuleBegin = avgPoint + (minProjection + avgRadius) * axis; - capsuleEnd = avgPoint + (maxProjection - avgRadius) * axis; + avgPoint /= (float)points.size(); + + // compute a k-Dop bounding volume + for (uint32_t j = 0; j < cardinalDirections.size(); ++j) { + float maxDot = -FLT_MAX; + float minDot = FLT_MIN; + for (uint32_t k = 0; k < points.size(); ++k) { + float kDot = glm::dot(cardinalDirections[j], points[k] - avgPoint); + if (kDot > maxDot) { + maxDot = kDot; + } + if (kDot < minDot) { + minDot = kDot; + } + } + joint.shapeInfo.points.push_back(avgPoint + maxDot * cardinalDirections[j]); + joint.shapeInfo.points.push_back(avgPoint + minDot * cardinalDirections[j]); } - - // save points for later - joint.shapeInfo.points.push_back(capsuleBegin); - joint.shapeInfo.points.push_back(capsuleEnd); } - joint.shapeInfo.radius = avgRadius; } geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString()); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 8014718815..0ddbc11bda 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -56,7 +56,6 @@ public: struct FBXJointShapeInfo { // same units and frame as FBXJoint.translation QVector points; - float radius; }; /// A single joint (transformation node) extracted from an FBX document. From 0e3d3451d7d2356f6df6b2ee890e08d4ebdfc8dd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 10:24:10 -0800 Subject: [PATCH 0998/1003] make keep alive pings the responsibility of NL --- interface/src/Application.cpp | 11 ----- interface/src/Application.h | 1 - interface/src/Menu.cpp | 1 - interface/src/Menu.h | 1 - interface/src/ui/Stats.cpp | 49 ++++++++------------ libraries/networking/src/LimitedNodeList.cpp | 16 ------- libraries/networking/src/LimitedNodeList.h | 1 - libraries/networking/src/NodeList.cpp | 16 +++++++ libraries/networking/src/NodeList.h | 4 ++ 9 files changed, 40 insertions(+), 60 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c77bb9a114..b1e5a676d6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -999,10 +999,6 @@ void Application::initializeGL() { connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); _entityEditSender.initialize(_enableProcessOctreeThread); - // call our timer function every second - connect(&pingTimer, &QTimer::timeout, this, &Application::ping); - pingTimer.start(1000); - _idleLoopStdev.reset(); // update before the first render @@ -2130,13 +2126,6 @@ bool Application::acceptSnapshot(const QString& urlString) { return true; } -// Every second, send a ping, if menu item is checked. -void Application::ping() { - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - DependencyManager::get()->sendPingPackets(); - } -} - void Application::idle(uint64_t now) { if (_aboutToQuit) { return; // bail early, nothing to do here. diff --git a/interface/src/Application.h b/interface/src/Application.h index 39e3879707..39f93f4b72 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -306,7 +306,6 @@ public slots: private slots: void clearDomainOctreeDetails(); - void ping(); void idle(uint64_t now); void aboutToQuit(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 24033325f6..0f97f5a975 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -502,7 +502,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); - addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index dfa2cfa41b..868afb0de8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -280,7 +280,6 @@ namespace MenuOption { const QString Stats = "Stats"; const QString StopAllScripts = "Stop All Scripts"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; - const QString TestPing = "Test Ping"; const QString ThirdPerson = "Third Person"; const QString ThreePointCalibration = "3 Point Calibration"; const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 1c0c03e16c..3127e00783 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -127,36 +127,27 @@ void Stats::updateStats(bool force) { STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); // Second column: ping - if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { - SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); - SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); - SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); - STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); - STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); - STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1); - - //// Now handle entity servers, since there could be more than one, we average their ping times - int totalPingOctree = 0; - int octreeServerCount = 0; - int pingOctreeMax = 0; - nodeList->eachNode([&](const SharedNodePointer& node) { - // TODO: this should also support entities - if (node->getType() == NodeType::EntityServer) { - totalPingOctree += node->getPingMs(); - octreeServerCount++; - if (pingOctreeMax < node->getPingMs()) { - pingOctreeMax = node->getPingMs(); - } - } - }); - - // update the entities ping with the average for all connected entity servers - STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1); - } else { - // -2 causes the QML to hide the ping column - STAT_UPDATE(audioPing, -2); - } + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); + SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); + SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); + STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); + STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1); + STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1); + //// Now handle entity servers, since there could be more than one, we average their ping times + int totalPingOctree = 0; + int octreeServerCount = 0; + int pingOctreeMax = 0; + nodeList->eachNode([&](const SharedNodePointer& node) { + // TODO: this should also support entities + if (node->getType() == NodeType::EntityServer) { + totalPingOctree += node->getPingMs(); + octreeServerCount++; + if (pingOctreeMax < node->getPingMs()) { + pingOctreeMax = node->getPingMs(); + } + } + }); // Third column, avatar stats MyAvatar* myAvatar = avatarManager->getMyAvatar(); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 4a7844ecc7..e717856ca2 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -700,22 +700,6 @@ void LimitedNodeList::sendSTUNRequest() { _nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); } -void LimitedNodeList::sendPingPackets() { - eachMatchingNode([](const SharedNodePointer& node)->bool { - switch (node->getType()) { - case NodeType::AvatarMixer: - case NodeType::AudioMixer: - case NodeType::EntityServer: - case NodeType::AssetServer: - return true; - default: - return false; - } - }, [&](const SharedNodePointer& node) { - sendPacket(constructPingPacket(), *node); - }); -} - void LimitedNodeList::processSTUNResponse(std::unique_ptr packet) { // check the cookie to make sure this is actually a STUN response // and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 26e648421a..8daeb3d256 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -228,7 +228,6 @@ public slots: void startSTUNPublicSocketUpdate(); virtual void sendSTUNRequest(); - void sendPingPackets(); bool killNodeWithUUID(const QUuid& nodeUUID); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index e03ac47854..1e2485345f 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -32,6 +32,7 @@ #include "udt/PacketHeaders.h" #include "SharedUtil.h" +const int KEEPALIVE_PING_INTERVAL_MS = 1000; NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) : LimitedNodeList(socketListenPort, dtlsListenPort), @@ -87,6 +88,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // anytime we get a new node we will want to attempt to punch to it connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); + + // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) + _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); + connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); + connect(&_domainHandler, SIGNAL(connectedToDomain()), &_keepAlivePingTimer, SLOT(start())); + connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // we definitely want STUN to update our public socket, so call the LNL to kick that off startSTUNPublicSocketUpdate(); @@ -632,3 +639,12 @@ void NodeList::activateSocketFromNodeCommunication(QSharedPointer pack flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket); } } + +void NodeList::sendKeepAlivePings() { + qDebug() << "Sending keepalive pings!"; + eachMatchingNode([this](const SharedNodePointer& node)->bool { + return _nodeTypesOfInterest.contains(node->getType()); + }, [&](const SharedNodePointer& node) { + sendPacket(constructPingPacket(), *node); + }); +} diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 5b9a4e5ae5..02f49d2918 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -85,6 +85,7 @@ public slots: void processPingReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processICEPingPacket(QSharedPointer packet); + signals: void limitOfSilentDomainCheckInsReached(); private slots: @@ -95,6 +96,8 @@ private slots: void handleNodePingTimeout(); void pingPunchForDomainServer(); + + void sendKeepAlivePings(); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -118,6 +121,7 @@ private: int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; bool _isShuttingDown { false }; + QTimer _keepAlivePingTimer; }; #endif // hifi_NodeList_h From ae88057ad9eca74be8a87664436a3003f313b010 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 10:24:10 -0800 Subject: [PATCH 0999/1003] remove dangling whitespace --- interface/src/avatar/SkeletonModel.cpp | 4 ++-- interface/src/avatar/SkeletonModel.h | 2 +- libraries/fbx/src/FBXReader.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c6c1ead8c1..99c2d0e041 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -664,7 +664,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha // draw a blue sphere at the capsule top point glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f); - deferredLighting->renderSolidSphereInstance(batch, + deferredLighting->renderSolidSphereInstance(batch, Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius), glm::vec4(0.6f, 0.6f, 0.8f, alpha)); @@ -672,7 +672,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f); glm::vec3 axis = topPoint - bottomPoint; - deferredLighting->renderSolidSphereInstance(batch, + deferredLighting->renderSolidSphereInstance(batch, Transform().setTranslation(bottomPoint).postScale(_boundingCapsuleRadius), glm::vec4(0.8f, 0.8f, 0.6f, alpha)); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index dc08168a8c..c1938097c3 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -82,7 +82,7 @@ public: bool getNeckPosition(glm::vec3& neckPosition) const; bool getLocalNeckPosition(glm::vec3& neckPosition) const; - + /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 3251b5b35d..a4482574e2 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1559,7 +1559,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS avgPoint += points[j]; } avgPoint /= (float)points.size(); - + // compute a k-Dop bounding volume for (uint32_t j = 0; j < cardinalDirections.size(); ++j) { float maxDot = -FLT_MAX; @@ -1568,7 +1568,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS float kDot = glm::dot(cardinalDirections[j], points[k] - avgPoint); if (kDot > maxDot) { maxDot = kDot; - } + } if (kDot < minDot) { minDot = kDot; } From d90c073102ee8a7d98f145827c3dc6b409ccaba5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 10:25:18 -0800 Subject: [PATCH 1000/1003] leverage NL keep alive pings in Agent --- assignment-client/src/Agent.cpp | 27 --------------------------- assignment-client/src/Agent.h | 2 -- 2 files changed, 29 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 1f56118177..8cc706992a 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -105,7 +105,6 @@ void Agent::handleAudioPacket(QSharedPointer packet) { } const QString AGENT_LOGGING_NAME = "agent"; -const int PING_INTERVAL = 1000; void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); @@ -127,10 +126,6 @@ void Agent::run() { << NodeType::MessagesMixer ); - _pingTimer = new QTimer(this); - connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests())); - _pingTimer->start(PING_INTERVAL); - // figure out the URL for the script for this agent assignment QUrl scriptURL; if (_payload.isEmpty()) { @@ -383,28 +378,6 @@ void Agent::aboutToFinish() { _scriptEngine->stop(); } - if (_pingTimer) { - _pingTimer->stop(); - delete _pingTimer; - } - // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); } - -void Agent::sendPingRequests() { - auto nodeList = DependencyManager::get(); - - nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool { - switch (node->getType()) { - case NodeType::AvatarMixer: - case NodeType::AudioMixer: - case NodeType::EntityServer: - return true; - default: - return false; - } - }, [nodeList](const SharedNodePointer& node) { - nodeList->sendPacket(nodeList->constructPingPacket(), *node); - }); -} diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index ab000015d5..fe1fffce5a 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -58,14 +58,12 @@ private slots: void handleAudioPacket(QSharedPointer packet); void handleOctreePacket(QSharedPointer packet, SharedNodePointer senderNode); void handleJurisdictionPacket(QSharedPointer packet, SharedNodePointer senderNode); - void sendPingRequests(); void processAgentAvatarAndAudio(float deltaTime); private: std::unique_ptr _scriptEngine; EntityEditPacketSender _entityEditSender; EntityTreeHeadlessViewer _entityViewer; - QTimer* _pingTimer; MixedAudioStream _receivedAudioStream; float _lastReceivedAudioLoudness; From c54dffac1232aa5d4d8df61911d449a6aeceac31 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 18 Nov 2015 10:32:35 -0800 Subject: [PATCH 1001/1003] fix entity-server avg ping, remove debug --- interface/src/ui/Stats.cpp | 3 +++ libraries/networking/src/NodeList.cpp | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 3127e00783..c379f31aab 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -148,6 +148,9 @@ void Stats::updateStats(bool force) { } } }); + + // update the entities ping with the average for all connected entity servers + STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1); // Third column, avatar stats MyAvatar* myAvatar = avatarManager->getMyAvatar(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 1e2485345f..925c64c77a 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -92,7 +92,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect) _keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings); - connect(&_domainHandler, SIGNAL(connectedToDomain()), &_keepAlivePingTimer, SLOT(start())); + connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start())); connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop); // we definitely want STUN to update our public socket, so call the LNL to kick that off @@ -641,7 +641,6 @@ void NodeList::activateSocketFromNodeCommunication(QSharedPointer pack } void NodeList::sendKeepAlivePings() { - qDebug() << "Sending keepalive pings!"; eachMatchingNode([this](const SharedNodePointer& node)->bool { return _nodeTypesOfInterest.contains(node->getType()); }, [&](const SharedNodePointer& node) { From 5c6df734c51a40f68cea2f8f51a7f19a4a062349 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 10:47:53 -0800 Subject: [PATCH 1002/1003] initialize vector of vec3's --- libraries/fbx/src/FBXReader.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a4482574e2..2d2b2c4b0a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1535,15 +1535,16 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS meshIDsToMeshIndices.insert(it.key(), meshIndex); } - ShapeVertices cardinalDirections; - cardinalDirections.push_back(Vectors::UNIT_X); - cardinalDirections.push_back(Vectors::UNIT_Y); - cardinalDirections.push_back(Vectors::UNIT_Z); const float INV_SQRT_3 = 0.57735026918f; - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3)); - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3)); - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3)); - cardinalDirections.push_back(glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)); + ShapeVertices cardinalDirections = { + Vectors::UNIT_X, + Vectors::UNIT_Y, + Vectors::UNIT_Z, + glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) + }; // now that all joints have been scanned compute a k-Dop bounding volume of mesh glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f); From 73055a59a78b1aa45dafc651275c5bdd601dd8d5 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Nov 2015 14:48:59 -0800 Subject: [PATCH 1003/1003] fix crash on login for bad boolean logic --- libraries/animation/src/AvatarRig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 4dbf5207b1..b72e15a0ce 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -13,7 +13,7 @@ /// Updates the state of the joint at the specified index. void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) { - if (index < 0 && index >= _jointStates.size()) { + if (index < 0 || index >= _jointStates.size()) { return; // bail } JointState& state = _jointStates[index];