From 3e126c051713ca19ca6dccad25f9c1f217f14e50 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 12 Jun 2017 17:14:13 +1200 Subject: [PATCH 01/18] Smoothly ease hands from uncontrolled to controlled positions --- libraries/animation/src/Rig.cpp | 59 ++++++++++++++++++++++++++++----- libraries/animation/src/Rig.h | 4 ++- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 20a2aab2b6..2fc8638d5f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1164,9 +1164,32 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f bool bodySensorTrackingEnabled = params.isLeftFootEnabled || params.isRightFootEnabled; const float RELAX_DURATION = 0.6f; + const float CONTROL_DURATION = 0.4f; if (params.isLeftEnabled) { + if (!_isLeftHandControlled) { + _leftHandControlDuration = CONTROL_DURATION; + _isLeftHandControlled = true; + } + glm::vec3 handPosition = params.leftPosition; + glm::quat handRotation = params.leftOrientation; + + if (_leftHandControlDuration > 0.0f) { + // Move hand from non-controlled position to controlled position. + _leftHandControlDuration = std::max(_leftHandControlDuration - dt, 0.0f); + auto ikNode = getAnimInverseKinematicsNode(); + if (ikNode) { + AnimPose handPose(Vectors::ONE, handRotation, handPosition); + float alpha = 1.0f - _leftHandControlDuration / CONTROL_DURATION; + const AnimPose geometryToRigTransform(_geometryToRigTransform); + AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); + ::blend(1, &uncontrolledHandPose, &handPose, alpha, &handPose); + handPosition = handPose.trans(); + handRotation = handPose.rot(); + } + } + if (!bodySensorTrackingEnabled) { // prevent the hand IK targets from intersecting the body capsule glm::vec3 displacement; @@ -1176,18 +1199,17 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f } _animVars.set("leftHandPosition", handPosition); - _animVars.set("leftHandRotation", params.leftOrientation); + _animVars.set("leftHandRotation", handRotation); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); - _isLeftHandControlled = true; - _lastLeftHandControlledPose = AnimPose(glm::vec3(1.0f), params.leftOrientation, handPosition); + _lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition); } else { if (_isLeftHandControlled) { _leftHandRelaxDuration = RELAX_DURATION; _isLeftHandControlled = false; } - if (_leftHandRelaxDuration > 0) { + if (_leftHandRelaxDuration > 0.0f) { // Move hand from controlled position to non-controlled position. _leftHandRelaxDuration = std::max(_leftHandRelaxDuration - dt, 0.0f); auto ikNode = getAnimInverseKinematicsNode(); @@ -1209,7 +1231,29 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f } if (params.isRightEnabled) { + if (!_isRightHandControlled) { + _rightHandControlDuration = CONTROL_DURATION; + _isRightHandControlled = true; + } + glm::vec3 handPosition = params.rightPosition; + glm::quat handRotation = params.rightOrientation; + + if (_rightHandControlDuration > 0.0f) { + // Move hand from non-controlled position to controlled position. + _rightHandControlDuration = std::max(_rightHandControlDuration - dt, 0.0f); + auto ikNode = getAnimInverseKinematicsNode(); + if (ikNode) { + AnimPose handPose(Vectors::ONE, handRotation, handPosition); + float alpha = 1.0f - _rightHandControlDuration / CONTROL_DURATION; + const AnimPose geometryToRigTransform(_geometryToRigTransform); + AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); + ::blend(1, &uncontrolledHandPose, &handPose, alpha, &handPose); + handPosition = handPose.trans(); + handRotation = handPose.rot(); + } + } + if (!bodySensorTrackingEnabled) { // prevent the hand IK targets from intersecting the body capsule glm::vec3 displacement; @@ -1219,18 +1263,17 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f } _animVars.set("rightHandPosition", handPosition); - _animVars.set("rightHandRotation", params.rightOrientation); + _animVars.set("rightHandRotation", handRotation); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); - _isRightHandControlled = true; - _lastRightHandControlledPose = AnimPose(glm::vec3(1.0f), params.rightOrientation, handPosition); + _lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition); } else { if (_isRightHandControlled) { _rightHandRelaxDuration = RELAX_DURATION; _isRightHandControlled = false; } - if (_rightHandRelaxDuration > 0) { + if (_rightHandRelaxDuration > 0.0f) { // Move hand from controlled position to non-controlled position. _rightHandRelaxDuration = std::max(_rightHandRelaxDuration - dt, 0.0f); auto ikNode = getAnimInverseKinematicsNode(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 994bd4b074..c8efd70a71 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -354,7 +354,9 @@ private: bool _isLeftHandControlled { false }; bool _isRightHandControlled { false }; - float _leftHandRelaxDuration { 0.0f }; + float _leftHandControlDuration{ 0.0f }; + float _rightHandControlDuration{ 0.0f }; + float _leftHandRelaxDuration{ 0.0f }; float _rightHandRelaxDuration { 0.0f }; AnimPose _lastLeftHandControlledPose; AnimPose _lastRightHandControlledPose; From 5eee2d83525687e7376e68e8db5c324b402b5eb3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 13 Jun 2017 12:39:46 +1200 Subject: [PATCH 02/18] Code review --- libraries/animation/src/Rig.cpp | 32 ++++++++++++++++---------------- libraries/animation/src/Rig.h | 8 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2fc8638d5f..0362c42b39 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1168,20 +1168,20 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f if (params.isLeftEnabled) { if (!_isLeftHandControlled) { - _leftHandControlDuration = CONTROL_DURATION; + _leftHandControlTimeRemaining = CONTROL_DURATION; _isLeftHandControlled = true; } glm::vec3 handPosition = params.leftPosition; glm::quat handRotation = params.leftOrientation; - if (_leftHandControlDuration > 0.0f) { + if (_leftHandControlTimeRemaining > 0.0f) { // Move hand from non-controlled position to controlled position. - _leftHandControlDuration = std::max(_leftHandControlDuration - dt, 0.0f); + _leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f); auto ikNode = getAnimInverseKinematicsNode(); if (ikNode) { AnimPose handPose(Vectors::ONE, handRotation, handPosition); - float alpha = 1.0f - _leftHandControlDuration / CONTROL_DURATION; + float alpha = 1.0f - _leftHandControlTimeRemaining / CONTROL_DURATION; const AnimPose geometryToRigTransform(_geometryToRigTransform); AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); ::blend(1, &uncontrolledHandPose, &handPose, alpha, &handPose); @@ -1205,16 +1205,16 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f _lastLeftHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition); } else { if (_isLeftHandControlled) { - _leftHandRelaxDuration = RELAX_DURATION; + _leftHandRelaxTimeRemaining = RELAX_DURATION; _isLeftHandControlled = false; } - if (_leftHandRelaxDuration > 0.0f) { + if (_leftHandRelaxTimeRemaining > 0.0f) { // Move hand from controlled position to non-controlled position. - _leftHandRelaxDuration = std::max(_leftHandRelaxDuration - dt, 0.0f); + _leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f); auto ikNode = getAnimInverseKinematicsNode(); if (ikNode) { - float alpha = 1.0f - _leftHandRelaxDuration / RELAX_DURATION; + float alpha = 1.0f - _leftHandRelaxTimeRemaining / RELAX_DURATION; const AnimPose geometryToRigTransform(_geometryToRigTransform); AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); AnimPose handPose; @@ -1232,20 +1232,20 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f if (params.isRightEnabled) { if (!_isRightHandControlled) { - _rightHandControlDuration = CONTROL_DURATION; + _rightHandControlTimeRemaining = CONTROL_DURATION; _isRightHandControlled = true; } glm::vec3 handPosition = params.rightPosition; glm::quat handRotation = params.rightOrientation; - if (_rightHandControlDuration > 0.0f) { + if (_rightHandControlTimeRemaining > 0.0f) { // Move hand from non-controlled position to controlled position. - _rightHandControlDuration = std::max(_rightHandControlDuration - dt, 0.0f); + _rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f); auto ikNode = getAnimInverseKinematicsNode(); if (ikNode) { AnimPose handPose(Vectors::ONE, handRotation, handPosition); - float alpha = 1.0f - _rightHandControlDuration / CONTROL_DURATION; + float alpha = 1.0f - _rightHandControlTimeRemaining / CONTROL_DURATION; const AnimPose geometryToRigTransform(_geometryToRigTransform); AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); ::blend(1, &uncontrolledHandPose, &handPose, alpha, &handPose); @@ -1269,16 +1269,16 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f _lastRightHandControlledPose = AnimPose(Vectors::ONE, handRotation, handPosition); } else { if (_isRightHandControlled) { - _rightHandRelaxDuration = RELAX_DURATION; + _rightHandRelaxTimeRemaining = RELAX_DURATION; _isRightHandControlled = false; } - if (_rightHandRelaxDuration > 0.0f) { + if (_rightHandRelaxTimeRemaining > 0.0f) { // Move hand from controlled position to non-controlled position. - _rightHandRelaxDuration = std::max(_rightHandRelaxDuration - dt, 0.0f); + _rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f); auto ikNode = getAnimInverseKinematicsNode(); if (ikNode) { - float alpha = 1.0f - _rightHandRelaxDuration / RELAX_DURATION; + float alpha = 1.0f - _rightHandRelaxTimeRemaining / RELAX_DURATION; const AnimPose geometryToRigTransform(_geometryToRigTransform); AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); AnimPose handPose; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c8efd70a71..1cb1dac4f3 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -354,10 +354,10 @@ private: bool _isLeftHandControlled { false }; bool _isRightHandControlled { false }; - float _leftHandControlDuration{ 0.0f }; - float _rightHandControlDuration{ 0.0f }; - float _leftHandRelaxDuration{ 0.0f }; - float _rightHandRelaxDuration { 0.0f }; + float _leftHandControlTimeRemaining { 0.0f }; + float _rightHandControlTimeRemaining { 0.0f }; + float _leftHandRelaxTimeRemaining { 0.0f }; + float _rightHandRelaxTimeRemaining { 0.0f }; AnimPose _lastLeftHandControlledPose; AnimPose _lastRightHandControlledPose; }; From 570ec8457d0f1e24dce30ac1e20e2984b6ad9406 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 10:45:30 +1200 Subject: [PATCH 03/18] Refactor --- libraries/animation/src/Rig.cpp | 66 +++++++++++++++++++-------------- libraries/animation/src/Rig.h | 3 ++ 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0362c42b39..fbb3d24298 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1165,6 +1165,10 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f const float RELAX_DURATION = 0.6f; const float CONTROL_DURATION = 0.4f; + const bool TO_CONTROLLED = true; + const bool FROM_CONTROLLED = false; + const bool LEFT_HAND = true; + const bool RIGHT_HAND = false; if (params.isLeftEnabled) { if (!_isLeftHandControlled) { @@ -1178,13 +1182,9 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f if (_leftHandControlTimeRemaining > 0.0f) { // Move hand from non-controlled position to controlled position. _leftHandControlTimeRemaining = std::max(_leftHandControlTimeRemaining - dt, 0.0f); - auto ikNode = getAnimInverseKinematicsNode(); - if (ikNode) { - AnimPose handPose(Vectors::ONE, handRotation, handPosition); - float alpha = 1.0f - _leftHandControlTimeRemaining / CONTROL_DURATION; - const AnimPose geometryToRigTransform(_geometryToRigTransform); - AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); - ::blend(1, &uncontrolledHandPose, &handPose, alpha, &handPose); + AnimPose handPose(Vectors::ONE, handRotation, handPosition); + if (transitionHandPose(_leftHandControlTimeRemaining, CONTROL_DURATION, handPose, LEFT_HAND, TO_CONTROLLED, + handPose)) { handPosition = handPose.trans(); handRotation = handPose.rot(); } @@ -1212,13 +1212,9 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f if (_leftHandRelaxTimeRemaining > 0.0f) { // Move hand from controlled position to non-controlled position. _leftHandRelaxTimeRemaining = std::max(_leftHandRelaxTimeRemaining - dt, 0.0f); - auto ikNode = getAnimInverseKinematicsNode(); - if (ikNode) { - float alpha = 1.0f - _leftHandRelaxTimeRemaining / RELAX_DURATION; - const AnimPose geometryToRigTransform(_geometryToRigTransform); - AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); - AnimPose handPose; - ::blend(1, &_lastLeftHandControlledPose, &uncontrolledHandPose, alpha, &handPose); + AnimPose handPose; + if (transitionHandPose(_leftHandRelaxTimeRemaining, RELAX_DURATION, _lastLeftHandControlledPose, LEFT_HAND, + FROM_CONTROLLED, handPose)) { _animVars.set("leftHandPosition", handPose.trans()); _animVars.set("leftHandRotation", handPose.rot()); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); @@ -1242,13 +1238,9 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f if (_rightHandControlTimeRemaining > 0.0f) { // Move hand from non-controlled position to controlled position. _rightHandControlTimeRemaining = std::max(_rightHandControlTimeRemaining - dt, 0.0f); - auto ikNode = getAnimInverseKinematicsNode(); - if (ikNode) { - AnimPose handPose(Vectors::ONE, handRotation, handPosition); - float alpha = 1.0f - _rightHandControlTimeRemaining / CONTROL_DURATION; - const AnimPose geometryToRigTransform(_geometryToRigTransform); - AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); - ::blend(1, &uncontrolledHandPose, &handPose, alpha, &handPose); + AnimPose handPose(Vectors::ONE, handRotation, handPosition); + if (transitionHandPose(_rightHandControlTimeRemaining, CONTROL_DURATION, handPose, RIGHT_HAND, TO_CONTROLLED, + handPose)) { handPosition = handPose.trans(); handRotation = handPose.rot(); } @@ -1276,13 +1268,9 @@ void Rig::updateFromHandAndFeetParameters(const HandAndFeetParameters& params, f if (_rightHandRelaxTimeRemaining > 0.0f) { // Move hand from controlled position to non-controlled position. _rightHandRelaxTimeRemaining = std::max(_rightHandRelaxTimeRemaining - dt, 0.0f); - auto ikNode = getAnimInverseKinematicsNode(); - if (ikNode) { - float alpha = 1.0f - _rightHandRelaxTimeRemaining / RELAX_DURATION; - const AnimPose geometryToRigTransform(_geometryToRigTransform); - AnimPose uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); - AnimPose handPose; - ::blend(1, &_lastRightHandControlledPose, &uncontrolledHandPose, alpha, &handPose); + AnimPose handPose; + if (transitionHandPose(_rightHandRelaxTimeRemaining, RELAX_DURATION, _lastRightHandControlledPose, RIGHT_HAND, + FROM_CONTROLLED, handPose)) { _animVars.set("rightHandPosition", handPose.trans()); _animVars.set("rightHandRotation", handPose.rot()); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); @@ -1588,3 +1576,25 @@ void Rig::computeAvatarBoundingCapsule( glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum))); localOffsetOut = rigCenter - (geometryToRig * rootPosition); } + +bool Rig::transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand, + bool isToControlled, AnimPose& returnHandPose) { + auto ikNode = getAnimInverseKinematicsNode(); + if (ikNode) { + float alpha = 1.0f - deltaTime / totalDuration; + const AnimPose geometryToRigTransform(_geometryToRigTransform); + AnimPose uncontrolledHandPose; + if (isLeftHand) { + uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledLeftHandPose(); + } else { + uncontrolledHandPose = geometryToRigTransform * ikNode->getUncontrolledRightHandPose(); + } + if (isToControlled) { + ::blend(1, &uncontrolledHandPose, &controlledHandPose, alpha, &returnHandPose); + } else { + ::blend(1, &controlledHandPose, &uncontrolledHandPose, alpha, &returnHandPose); + } + return true; + } + return false; +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1cb1dac4f3..b5b69fc018 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -352,6 +352,9 @@ private: int _nextStateHandlerId { 0 }; QMutex _stateMutex; + bool transitionHandPose(float deltaTime, float totalDuration, AnimPose& controlledHandPose, bool isLeftHand, + bool isToControlled, AnimPose& returnHandPose); + bool _isLeftHandControlled { false }; bool _isRightHandControlled { false }; float _leftHandControlTimeRemaining { 0.0f }; From 05f19d54f4b7ddabb6039b125d902dc4262b6092 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 11:55:04 +1200 Subject: [PATCH 04/18] Add HMD roll control JavaScript API --- interface/src/avatar/MyAvatar.h | 17 +++++++++++++++++ scripts/developer/hmdRollControlDisable.js | 17 +++++++++++++++++ scripts/developer/hmdRollControlEnable.js | 21 +++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 scripts/developer/hmdRollControlDisable.js create mode 100644 scripts/developer/hmdRollControlEnable.js diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index cfe66eb10e..bfc7cd1085 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -132,6 +132,10 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls) + Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled) + Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) + Q_PROPERTY(float hmdRollControlSpeed READ getHMDRollControlSpeed WRITE setHMDRollControlSpeed) + public: enum DriveKeys { TRANSLATE_X = 0, @@ -337,6 +341,13 @@ public: void setUseAdvancedMovementControls(bool useAdvancedMovementControls) { _useAdvancedMovementControls.set(useAdvancedMovementControls); } + void setHMDRollControlEnabled(bool value) { _hmdRollControlEnabled = value; } + bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; } + void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; } + float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; } + void setHMDRollControlSpeed(float value) { _hmdRollControlSpeed = value; } + float getHMDRollControlSpeed() const { return _hmdRollControlSpeed; } + // get/set avatar data void saveData(); void loadData(); @@ -687,6 +698,12 @@ private: bool _useSnapTurn { true }; bool _clearOverlayWhenMoving { true }; + const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg + const float ROLL_CONTROL_SPEED_DEFAULT = 2.5f; // deg/sec/deg + bool _hmdRollControlEnabled { true }; + float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; + float _hmdRollControlSpeed { ROLL_CONTROL_SPEED_DEFAULT }; + // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; diff --git a/scripts/developer/hmdRollControlDisable.js b/scripts/developer/hmdRollControlDisable.js new file mode 100644 index 0000000000..9d87a62264 --- /dev/null +++ b/scripts/developer/hmdRollControlDisable.js @@ -0,0 +1,17 @@ +// +// hmdRollControlDisable.js +// +// Created by David Rowe on 4 Jun 2017. +// Copyright 2017 High Fidelity, 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 hmdRollControlEnabled = false; + +print("HMD roll control: " + hmdRollControlEnabled); + +MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; + +Script.stop(); diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js new file mode 100644 index 0000000000..d18e099b44 --- /dev/null +++ b/scripts/developer/hmdRollControlEnable.js @@ -0,0 +1,21 @@ +// +// hmdRollControlEnable.js +// +// Created by David Rowe on 4 Jun 2017. +// Copyright 2017 High Fidelity, 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 hmdRollControlEnabled = true; +var hmdRollControlDeadZone = 8.0; // deg +var hmdRollControlSpeed = 1.0; // deg/sec/deg + +print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); + +MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; +MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; +MyAvatar.hmdRollControlSpeed = hmdRollControlSpeed; + +Script.stop(); From 6271b8ee7ca8f344ac49322b82aad985161574e7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 13 Jun 2017 16:56:18 -0700 Subject: [PATCH 05/18] avatar whitelist --- assignment-client/src/avatars/AvatarMixer.cpp | 55 +++++++++++++++++-- assignment-client/src/avatars/AvatarMixer.h | 7 ++- .../src/avatars/AvatarMixerClientData.h | 3 + .../resources/describe-settings.json | 16 ++++++ libraries/avatars/src/AvatarData.cpp | 3 +- libraries/avatars/src/AvatarData.h | 7 +-- libraries/avatars/src/AvatarHashMap.cpp | 3 +- 7 files changed, 83 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 870149f1bc..b1841eb4e5 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -132,7 +132,7 @@ void AvatarMixer::start() { auto start = usecTimestampNow(); nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { std::for_each(cbegin, cend, [&](const SharedNodePointer& node) { - manageDisplayName(node); + manageIdentityData(node); ++_sumListeners; }); }, &lockWait, &nodeTransform, &functor); @@ -183,8 +183,9 @@ void AvatarMixer::start() { // NOTE: nodeData->getAvatar() might be side effected, must be called when access to node/nodeData // is guaranteed to not be accessed by other thread -void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { +void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); + bool sendIdentity = false; if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) { AvatarData& avatar = nodeData->getAvatar(); const QString& existingBaseDisplayName = nodeData->getBaseDisplayName(); @@ -210,9 +211,36 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { soFar.second++; // refcount nodeData->flagIdentityChange(); nodeData->setAvatarSessionDisplayNameMustChange(false); - sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name. + sendIdentity = true; qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } + if (nodeData && nodeData->getAvatarSkeletonModelUrlMustChange()) { // never true for an empty _avatarWhitelist + nodeData->setAvatarSkeletonModelUrlMustChange(false); + AvatarData& avatar = nodeData->getAvatar(); + static const QUrl emptyURL(""); + QUrl url = avatar.cannonicalSkeletonModelURL(emptyURL); + if (!isAvatarInWhitelist(url)) { + qCDebug(avatars) << "Forbidden avatar" << nodeData->getNodeID() << avatar.getSkeletonModelURL() << "replaced with" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + avatar.setSkeletonModelURL(_replacementAvatar); + sendIdentity = true; + } + } + if (sendIdentity) { + sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar. + } +} + +bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { + for (const auto& whiteListedPrefix : _avatarWhitelist) { + auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + // check if this script URL matches the whitelist domain and, optionally, is beneath the path + if (url.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && + url.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { + return true; + } + } + + return false; } void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) { @@ -402,13 +430,17 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes AvatarData::parseAvatarIdentityPacket(message->getMessage(), identity); bool identityChanged = false; bool displayNameChanged = false; - avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged); + bool skeletonModelUrlChanged = false; + avatar.processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); nodeData->flagIdentityChange(); if (displayNameChanged) { nodeData->setAvatarSessionDisplayNameMustChange(true); } + if (skeletonModelUrlChanged && !_avatarWhitelist.isEmpty()) { + nodeData->setAvatarSkeletonModelUrlMustChange(true); + } } } } @@ -764,4 +796,19 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale << "and a maximum avatar scale of" << _domainMaximumScale; + const QString AVATAR_WHITELIST_DEFAULT{ "" }; + static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; + _avatarWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION].toString(AVATAR_WHITELIST_DEFAULT).split(',', QString::KeepEmptyParts); + + static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar"; + _replacementAvatar = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION].toString(REPLACEMENT_AVATAR_DEFAULT); + + if ((_avatarWhitelist.count() == 1) && _avatarWhitelist[0].isEmpty()) { + _avatarWhitelist.clear(); // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + } + if (_avatarWhitelist.isEmpty()) { + qCDebug(avatars) << "All avatars are allowed."; + } else { + qCDebug(avatars) << "Avatars other than" << _avatarWhitelist << "will be replaced by" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 1925ec1ebd..f8ebe419a9 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -59,7 +59,12 @@ private: void parseDomainServerSettings(const QJsonObject& domainSettings); void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); - void manageDisplayName(const SharedNodePointer& node); + void manageIdentityData(const SharedNodePointer& node); + bool isAvatarInWhitelist(const QUrl& url); + + const QString REPLACEMENT_AVATAR_DEFAULT{ "" }; + QStringList _avatarWhitelist { }; + QString _replacementAvatar { REPLACEMENT_AVATAR_DEFAULT }; p_high_resolution_clock::time_point _lastFrameTimestamp; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index c905b10251..cf2cdbfc23 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -65,6 +65,8 @@ public: void flagIdentityChange() { _identityChangeTimestamp = usecTimestampNow(); } bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } + bool getAvatarSkeletonModelUrlMustChange() const { return _avatarSkeletonModelUrlMustChange; } + void setAvatarSkeletonModelUrlMustChange(bool set = true) { _avatarSkeletonModelUrlMustChange = set; } void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } @@ -146,6 +148,7 @@ private: uint64_t _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ true }; + bool _avatarSkeletonModelUrlMustChange{ true }; int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a8c6dd84e7..a16d877f1c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -866,6 +866,22 @@ "help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.", "placeholder": 3.0, "default": 3.0 + }, + { + "name": "avatar_whitelist", + "label": "Avatars Allowed from:", + "help": "Comma separated list of URLs (with optional paths) that avatar .fst files are allowed from. If someone attempts to use an avatar with a different domain, it will be rejected and the replacement avatar will be used. If left blank, any domain is allowed.", + "placeholder": "", + "default": "", + "advanced": true + }, + { + "name": "replacement_avatar", + "label": "Replacement Avatar for disallowed avatars", + "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic defalt avatar is used.", + "placeholder": "", + "default": "", + "advanced": true } ] }, diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4407e12295..7731d53ec3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1504,7 +1504,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; } -void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged) { +void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged) { if (identity.sequenceId < _identitySequenceId) { qCDebug(avatars) << "Ignoring older identity packet for avatar" << getSessionUUID() @@ -1517,6 +1517,7 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { setSkeletonModelURL(identity.skeletonModelURL); identityChanged = true; + skeletonModelUrlChanged = true; if (_firstSkeletonCheck) { displayNameChanged = true; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 4104615cfe..c64d7a00b8 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -368,6 +368,7 @@ public: virtual ~AvatarData(); static const QUrl& defaultFullAvatarModelUrl(); + QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; virtual bool isMyAvatar() const { return false; } @@ -536,9 +537,8 @@ public: static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); - // identityChanged returns true if identity has changed, false otherwise. - // displayNameChanged returns true if displayName has changed, false otherwise. - void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged); + // identityChanged returns true if identity has changed, false otherwise. Similarly for displaNameChanged and skeletonModelUrlChange. + void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged); QByteArray identityByteArray() const; @@ -697,7 +697,6 @@ protected: QVector _attachmentData; QString _displayName; QString _sessionDisplayName { }; - QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; QHash _jointIndices; ///< 1-based, since zero is returned for missing keys QStringList _jointNames; ///< in order of depth-first traversal diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 2ccc64fee2..fb954f4731 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -148,8 +148,9 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identity.uuid, sendingNode); bool identityChanged = false; bool displayNameChanged = false; + bool skeletonModelUrlChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. - avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged); + avatar->processAvatarIdentity(identity, identityChanged, displayNameChanged, skeletonModelUrlChanged); } } From be2bcb1c13d4f90739620637d3f859f9c41cfd84 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 12:20:46 +1200 Subject: [PATCH 06/18] Turn with head roll in HMD when walking or flying --- interface/src/avatar/MyAvatar.cpp | 26 ++++++++++++++++++++++++++ interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 27 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 24a25f314d..bd217a98a2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1961,6 +1961,32 @@ void MyAvatar::updateOrientation(float deltaTime) { totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI)); } + // Use head/HMD roll to turn while walking or flying. + if (qApp->isHMDMode() && _hmdRollControlEnabled) { + // Turn with head roll. + const float MIN_CONTROL_SPEED = 0.01f; + float speed = glm::length(getVelocity()); + if (speed >= MIN_CONTROL_SPEED) { + // Feather turn when stopping moving. + float speedFactor; + if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { + _lastDrivenSpeed = speed; + speedFactor = 1.0; + } else { + speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); + } + + float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; + + float rollAngle = glm::degrees(asin(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); + float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; + rollAngle = fabsf(rollAngle); + + float yawChange = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; + totalBodyYaw += speedFactor * direction * yawChange * deltaTime * _hmdRollControlSpeed; + } + } + // update body orientation by movement inputs glm::quat initialOrientation = getOrientationOutbound(); setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index bfc7cd1085..8039674605 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -703,6 +703,7 @@ private: bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; float _hmdRollControlSpeed { ROLL_CONTROL_SPEED_DEFAULT }; + float _lastDrivenSpeed { 0.0f }; // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; From 395d40f0f9026f4ef3935522b7a2a02f4d3405b0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 12:21:19 +1200 Subject: [PATCH 07/18] Fix script default to match C++ default --- scripts/developer/hmdRollControlEnable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js index d18e099b44..3cdc02a466 100644 --- a/scripts/developer/hmdRollControlEnable.js +++ b/scripts/developer/hmdRollControlEnable.js @@ -10,7 +10,7 @@ var hmdRollControlEnabled = true; var hmdRollControlDeadZone = 8.0; // deg -var hmdRollControlSpeed = 1.0; // deg/sec/deg +var hmdRollControlSpeed = 2.5; // deg/sec/deg print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); From 87f3ce7699031d751c75102010df67a226c167e5 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 13 Jun 2017 19:30:01 -0700 Subject: [PATCH 08/18] reset avatar on domain change if the last domain imposed something on us --- interface/src/Application.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ce6cc9b25..f1bef1a23b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5292,6 +5292,11 @@ void Application::nodeActivated(SharedNodePointer node) { if (node->getType() == NodeType::AvatarMixer) { // new avatar mixer, send off our identity packet on next update loop + // Reset skeletonModelUrl if the last server modified our choice. + static const QUrl empty{}; + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->cannonicalSkeletonModelURL(empty)) { + getMyAvatar()->resetFullAvatarURL(); + } getMyAvatar()->markIdentityDataChanged(); getMyAvatar()->resetLastSent(); } From e3b4e4aa2045ac14ba7c31350164192a1b193798 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 Jun 2017 15:37:36 +1200 Subject: [PATCH 09/18] Disable print statements --- scripts/developer/hmdRollControlDisable.js | 2 +- scripts/developer/hmdRollControlEnable.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/developer/hmdRollControlDisable.js b/scripts/developer/hmdRollControlDisable.js index 9d87a62264..fe8a85e3e5 100644 --- a/scripts/developer/hmdRollControlDisable.js +++ b/scripts/developer/hmdRollControlDisable.js @@ -10,7 +10,7 @@ var hmdRollControlEnabled = false; -print("HMD roll control: " + hmdRollControlEnabled); +//print("HMD roll control: " + hmdRollControlEnabled); MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js index 3cdc02a466..8af6cca6a5 100644 --- a/scripts/developer/hmdRollControlEnable.js +++ b/scripts/developer/hmdRollControlEnable.js @@ -12,7 +12,7 @@ var hmdRollControlEnabled = true; var hmdRollControlDeadZone = 8.0; // deg var hmdRollControlSpeed = 2.5; // deg/sec/deg -print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); +//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; From b3091de5369755df9730abaa6e9a361e9e26bf62 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 14 Jun 2017 11:16:55 -0700 Subject: [PATCH 10/18] forgot proper initial value of flag after changing whitelist to be actual list --- assignment-client/src/avatars/AvatarMixerClientData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index cf2cdbfc23..12b0286088 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -148,7 +148,7 @@ private: uint64_t _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ true }; - bool _avatarSkeletonModelUrlMustChange{ true }; + bool _avatarSkeletonModelUrlMustChange{ false }; int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; From 2deb26551f03e7bfc9cdcd022267781a83fcf052 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 14 Jun 2017 11:56:47 -0700 Subject: [PATCH 11/18] typos in comments/help-strings --- domain-server/resources/describe-settings.json | 2 +- libraries/avatars/src/AvatarData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index a16d877f1c..c5e9b08143 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -878,7 +878,7 @@ { "name": "replacement_avatar", "label": "Replacement Avatar for disallowed avatars", - "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic defalt avatar is used.", + "help": "A URL for an avatar .fst to be used when someone tries to use an avatar that is not allowed. If left blank, the generic default avatar is used.", "placeholder": "", "default": "", "advanced": true diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c64d7a00b8..8941d9d95f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -537,7 +537,7 @@ public: static void parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut); - // identityChanged returns true if identity has changed, false otherwise. Similarly for displaNameChanged and skeletonModelUrlChange. + // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const Identity& identity, bool& identityChanged, bool& displayNameChanged, bool& skeletonModelUrlChanged); QByteArray identityByteArray() const; From 665870b846250aa136a1cc61dfc509f886102514 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 14 Jun 2017 12:06:29 -0700 Subject: [PATCH 12/18] comment per cr --- assignment-client/src/avatars/AvatarMixer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b1841eb4e5..c8b68a740c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -231,6 +231,9 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { } bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { + // The avatar is in the whitelist if: + // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND + // 2. The avatar's URL's path starts with the path of that same URL in the whitelist for (const auto& whiteListedPrefix : _avatarWhitelist) { auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); // check if this script URL matches the whitelist domain and, optionally, is beneath the path From 7a6df850c5777c737bccb1f841fad6076cbb0a99 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 14 Jun 2017 13:22:46 -0700 Subject: [PATCH 13/18] Fix crash on exit when tablet is open --- interface/src/Application.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ce6cc9b25..56e8c8e2fb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1721,6 +1721,10 @@ void Application::cleanupBeforeQuit() { // Cleanup all overlays after the scripts, as scripts might add more _overlays.cleanupAllOverlays(); + // The cleanup process enqueues the transactions but does not process them. Calling this here will force the actual + // removal of the items. + // See https://highfidelity.fogbugz.com/f/cases/5328 + _main3DScene->processTransactionQueue(); // first stop all timers directly or by invokeMethod // depending on what thread they run in From 55e0082792605ee4713d7bc14ce96d77617466d7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 15 Jun 2017 09:10:08 +1200 Subject: [PATCH 14/18] Code review --- interface/src/avatar/MyAvatar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bd217a98a2..b2c469f614 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1971,19 +1971,19 @@ void MyAvatar::updateOrientation(float deltaTime) { float speedFactor; if (getDriveKey(TRANSLATE_Z) != 0.0f || _lastDrivenSpeed == 0.0f) { _lastDrivenSpeed = speed; - speedFactor = 1.0; + speedFactor = 1.0f; } else { speedFactor = glm::min(speed / _lastDrivenSpeed, 1.0f); } float direction = glm::dot(getVelocity(), getRotation() * Vectors::UNIT_NEG_Z) > 0.0f ? 1.0f : -1.0f; - float rollAngle = glm::degrees(asin(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); + float rollAngle = glm::degrees(asinf(glm::dot(IDENTITY_UP, _hmdSensorOrientation * IDENTITY_RIGHT))); float rollSign = rollAngle < 0.0f ? -1.0f : 1.0f; rollAngle = fabsf(rollAngle); + rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - float yawChange = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - totalBodyYaw += speedFactor * direction * yawChange * deltaTime * _hmdRollControlSpeed; + totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlSpeed; } } From 16bebdd40945cf19878abf364f8dc4ad56f02e18 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 15 Jun 2017 09:20:55 +1200 Subject: [PATCH 15/18] Rename identifiers --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MyAvatar.h | 10 +++++----- scripts/developer/hmdRollControlEnable.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b2c469f614..1adcfbd345 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1983,7 +1983,7 @@ void MyAvatar::updateOrientation(float deltaTime) { rollAngle = fabsf(rollAngle); rollAngle = rollAngle > _hmdRollControlDeadZone ? rollSign * (rollAngle - _hmdRollControlDeadZone) : 0.0f; - totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlSpeed; + totalBodyYaw += speedFactor * direction * rollAngle * deltaTime * _hmdRollControlRate; } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8039674605..f61f24fb11 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -134,7 +134,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled) Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) - Q_PROPERTY(float hmdRollControlSpeed READ getHMDRollControlSpeed WRITE setHMDRollControlSpeed) + Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) public: enum DriveKeys { @@ -345,8 +345,8 @@ public: bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; } void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; } float getHMDRollControlDeadZone() const { return _hmdRollControlDeadZone; } - void setHMDRollControlSpeed(float value) { _hmdRollControlSpeed = value; } - float getHMDRollControlSpeed() const { return _hmdRollControlSpeed; } + void setHMDRollControlRate(float value) { _hmdRollControlRate = value; } + float getHMDRollControlRate() const { return _hmdRollControlRate; } // get/set avatar data void saveData(); @@ -699,10 +699,10 @@ private: bool _clearOverlayWhenMoving { true }; const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // deg - const float ROLL_CONTROL_SPEED_DEFAULT = 2.5f; // deg/sec/deg + const float ROLL_CONTROL_RATE_DEFAULT = 2.5f; // deg/sec/deg bool _hmdRollControlEnabled { true }; float _hmdRollControlDeadZone { ROLL_CONTROL_DEAD_ZONE_DEFAULT }; - float _hmdRollControlSpeed { ROLL_CONTROL_SPEED_DEFAULT }; + float _hmdRollControlRate { ROLL_CONTROL_RATE_DEFAULT }; float _lastDrivenSpeed { 0.0f }; // working copies -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access diff --git a/scripts/developer/hmdRollControlEnable.js b/scripts/developer/hmdRollControlEnable.js index 8af6cca6a5..81318b7ddd 100644 --- a/scripts/developer/hmdRollControlEnable.js +++ b/scripts/developer/hmdRollControlEnable.js @@ -10,12 +10,12 @@ var hmdRollControlEnabled = true; var hmdRollControlDeadZone = 8.0; // deg -var hmdRollControlSpeed = 2.5; // deg/sec/deg +var hmdRollControlRate = 2.5; // deg/sec/deg -//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlSpeed); +//print("HMD roll control: " + hmdRollControlEnabled + ", " + hmdRollControlDeadZone + ", " + hmdRollControlRate); MyAvatar.hmdRollControlEnabled = hmdRollControlEnabled; MyAvatar.hmdRollControlDeadZone = hmdRollControlDeadZone; -MyAvatar.hmdRollControlSpeed = hmdRollControlSpeed; +MyAvatar.hmdRollControlRate = hmdRollControlRate; Script.stop(); From 9fa21fc79f8dc2ac6330327037b6da2fcd646819 Mon Sep 17 00:00:00 2001 From: seefo Date: Wed, 14 Jun 2017 14:45:25 -0700 Subject: [PATCH 16/18] Incomplete sandbox content updates are now removed and reperformed --- server-console/src/main.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 408a17bd56..95fb0d81b2 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -101,6 +101,10 @@ function getApplicationDataDirectory() { return path.join(getRootHifiDataDirectory(), '/Server Console'); } +// Update lock filepath +const UPDATER_LOCK_FILENAME = ".updating"; +const UPDATER_LOCK_FULL_PATH = getRootHifiDataDirectory() + "/" + UPDATER_LOCK_FILENAME; + // Configure log global.log = require('electron-log'); const logFile = getApplicationDataDirectory() + '/log.txt'; @@ -630,11 +634,22 @@ function checkNewContent() { userConfig.save(configPath); } }); + } else if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { + backupResourceDirectoriesAndRestart(); } } }); } +function removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory) { + if (fs.existsSync(UPDATER_LOCK_FULL_PATH)) { + log.debug('Removing incomplete content update files before copying new update'); + fs.emptyDirSync(dsResourceDirectory); + fs.emptyDirSync(acResourceDirectory); + } else { + fs.ensureFileSync(UPDATER_LOCK_FULL_PATH); + } +} function maybeInstallDefaultContentSet(onComplete) { // Check for existing data @@ -673,7 +688,11 @@ function maybeInstallDefaultContentSet(onComplete) { } log.debug("Found contentPath:" + argv.contentPath); + if (argv.contentPath) { + // check if we're updating a data folder whose update is incomplete + removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory); + fs.copy(argv.contentPath, getRootHifiDataDirectory(), function (err) { if (err) { log.debug('Could not copy home content: ' + err); @@ -682,12 +701,12 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug('Copied home content over to: ' + getRootHifiDataDirectory()); userConfig.set('homeContentLastModified', new Date()); userConfig.save(configPath); + fs.removeSync(UPDATER_LOCK_FULL_PATH); onComplete(); }); return; } - // Show popup var window = new BrowserWindow({ icon: appIcon, @@ -718,6 +737,9 @@ function maybeInstallDefaultContentSet(onComplete) { var aborted = false; + // check if we're updating a data folder whose update is incomplete + removeIncompleteUpdate(acResourceDirectory, dsResourceDirectory); + // Start downloading content set var req = progress(request.get({ url: HOME_CONTENT_URL @@ -763,6 +785,7 @@ function maybeInstallDefaultContentSet(onComplete) { log.debug("Finished unarchiving home content set"); userConfig.set('homeContentLastModified', new Date()); userConfig.save(configPath); + fs.removeSync(UPDATER_LOCK_FULL_PATH); sendStateUpdate('complete'); }); From d352163d77515ccc1bcde9c92a35147172d06c1d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 15 Jun 2017 09:24:14 -0700 Subject: [PATCH 17/18] fix no audio device from previously removed audio device --- .../AudioDeviceScriptingInterface.cpp | 24 +++++++++++++++---- libraries/audio-client/src/AudioClient.cpp | 5 ++++ libraries/audio-client/src/AudioClient.h | 2 ++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.cpp b/interface/src/scripting/AudioDeviceScriptingInterface.cpp index d22f948344..1fd30af0e3 100644 --- a/interface/src/scripting/AudioDeviceScriptingInterface.cpp +++ b/interface/src/scripting/AudioDeviceScriptingInterface.cpp @@ -48,15 +48,31 @@ AudioDeviceScriptingInterface::AudioDeviceScriptingInterface(): QAbstractListMod SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); const QString inDevice = settings->getValue("audio_input_device", _currentInputDevice).toString(); if (inDevice != _currentInputDevice) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice; - setInputDeviceAsync(inDevice); + // before using the old setting, check to make sure the device still exists.... + bool inDeviceExists = DependencyManager::get()->getNamedAudioDeviceForModeExists(QAudio::AudioInput, inDevice); + + if (inDeviceExists) { + qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice; + setInputDeviceAsync(inDevice); + } else { + qCDebug(audioclient) << __FUNCTION__ << "previously selected device no longer exists inDevice: [" << inDevice << "] keeping device _currentInputDevice:" << _currentInputDevice; + } } // If the audio_output_device setting is not available, use the _currentOutputDevice auto outDevice = settings->getValue("audio_output_device", _currentOutputDevice).toString(); + if (outDevice != _currentOutputDevice) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice; - setOutputDeviceAsync(outDevice); + // before using the old setting, check to make sure the device still exists.... + bool outDeviceExists = DependencyManager::get()->getNamedAudioDeviceForModeExists(QAudio::AudioOutput, outDevice); + + if (outDeviceExists) { + qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice; + setOutputDeviceAsync(outDevice); + } else { + qCDebug(audioclient) << __FUNCTION__ << "previously selected device no longer exists outDevice: [" << outDevice << "] keeping device _currentOutputDevice:" << _currentOutputDevice; + } + } } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 0bc72ae689..5297426840 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -395,6 +395,11 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice(); } +bool AudioClient::getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName) { + return (getNamedAudioDeviceForMode(mode, deviceName).deviceName() == deviceName); +} + + // attempt to use the native sample rate and channel count bool nativeFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, QAudioFormat& audioFormat) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index bec2fd2cc9..f3e7e418b2 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -149,6 +149,8 @@ public: static const float CALLBACK_ACCELERATOR_RATIO; + bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName); + #ifdef Q_OS_WIN static QString friendlyNameForAudioDevice(wchar_t* guid); #endif From 023ca8562301eb3fbd7c235afb1d7b17112b969b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 15 Jun 2017 09:27:43 -0700 Subject: [PATCH 18/18] remove chat from default scripts --- scripts/defaultScripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index aef8d9d85b..319a821eb6 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -33,7 +33,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", - "system/chat.js" + // "system/chat.js" ]; // add a menu item for debugging