diff --git a/interface/resources/icons/tablet-icons/raise-hand-a.svg b/interface/resources/icons/tablet-icons/raise-hand-a.svg
new file mode 100644
index 0000000000..fd35073332
--- /dev/null
+++ b/interface/resources/icons/tablet-icons/raise-hand-a.svg
@@ -0,0 +1,70 @@
+
+
+
+
diff --git a/interface/resources/icons/tablet-icons/raise-hand-i.svg b/interface/resources/icons/tablet-icons/raise-hand-i.svg
new file mode 100644
index 0000000000..50a6aa2606
--- /dev/null
+++ b/interface/resources/icons/tablet-icons/raise-hand-i.svg
@@ -0,0 +1,60 @@
+
+
+
+
diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml
index fc108f47e3..c1bd35f49d 100644
--- a/interface/resources/qml/hifi/Feed.qml
+++ b/interface/resources/qml/hifi/Feed.qml
@@ -238,8 +238,25 @@ Column {
stackShadowNarrowing: root.stackShadowNarrowing;
shadowHeight: root.stackedCardShadowHeight;
- hoverThunk: function () { scroll.currentIndex = index; }
- unhoverThunk: function () { scroll.currentIndex = -1; }
+ hoverThunk: function () { scrollToIndex(index); }
+ unhoverThunk: function () { scrollToIndex(-1); }
}
}
+ NumberAnimation {
+ id: anim;
+ target: scroll;
+ property: "contentX";
+ duration: 250;
+ }
+ function scrollToIndex(index) {
+ anim.running = false;
+ var pos = scroll.contentX;
+ var destPos;
+ scroll.positionViewAtIndex(index, ListView.Contain);
+ destPos = scroll.contentX;
+ anim.from = pos;
+ anim.to = destPos;
+ scroll.currentIndex = index;
+ anim.running = true;
+ }
}
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index 61a283b88c..4970112405 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -408,6 +408,7 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickR
const QVector& overlaysToInclude,
const QVector& overlaysToDiscard,
bool visibleOnly, bool collidableOnly) {
+ QReadLocker lock(&_lock);
float bestDistance = std::numeric_limits::max();
bool bestIsFront = false;
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index a5b742c32c..ccc843b5cc 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -63,7 +63,7 @@ static glm::mat4 computeOffset(glm::mat4 defaultToReferenceMat, glm::mat4 defaul
}
static bool sortPucksYPosition(std::pair firstPuck, std::pair secondPuck) {
- return (firstPuck.second.translation.y < firstPuck.second.translation.y);
+ return (firstPuck.second.translation.y < secondPuck.second.translation.y);
}
static QString deviceTrackingResultToString(vr::ETrackingResult trackingResult) {
@@ -280,6 +280,7 @@ void ViveControllerManager::InputDevice::calibrateOrUncalibrate(const controller
}
void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibrationData& inputCalibration) {
+ qDebug() << "Puck Calibration: Starting...";
// convert the hmd head from sensor space to avatar space
glm::mat4 hmdSensorFlippedMat = inputCalibration.hmdSensorMat * Matrices::Y_180;
glm::mat4 sensorToAvatarMat = glm::inverse(inputCalibration.avatarMat) * inputCalibration.sensorToWorldMat;
@@ -299,18 +300,24 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
glm::mat4 defaultToReferenceMat = currentHead * glm::inverse(inputCalibration.defaultHeadMat);
int puckCount = (int)_validTrackedObjects.size();
+ qDebug() << "Puck Calibration: " << puckCount << " pucks found for calibration";
_config = _preferedConfig;
if (_config != Config::Auto && puckCount < MIN_PUCK_COUNT) {
+ qDebug() << "Puck Calibration: Failed: Could not meet the minimal # of pucks";
uncalibrate();
return;
} else if (_config == Config::Auto){
if (puckCount == MIN_PUCK_COUNT) {
_config = Config::Feet;
+ qDebug() << "Puck Calibration: Auto Config: " << configToString(_config) << " configuration";
} else if (puckCount == MIN_FEET_AND_HIPS) {
_config = Config::FeetAndHips;
+ qDebug() << "Puck Calibration: Auto Config: " << configToString(_config) << " configuration";
} else if (puckCount >= MIN_FEET_HIPS_CHEST) {
_config = Config::FeetHipsAndChest;
+ qDebug() << "Puck Calibration: Auto Config: " << configToString(_config) << " configuration";
} else {
+ qDebug() << "Puck Calibration: Auto Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
return;
}
@@ -318,8 +325,6 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
std::sort(_validTrackedObjects.begin(), _validTrackedObjects.end(), sortPucksYPosition);
-
-
auto& firstFoot = _validTrackedObjects[FIRST_FOOT];
auto& secondFoot = _validTrackedObjects[SECOND_FOOT];
controller::Pose& firstFootPose = firstFoot.second;
@@ -349,10 +354,12 @@ void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibr
_jointToPuckMap[controller::SPINE2] = _validTrackedObjects[CHEST].first;
_pucksOffset[_validTrackedObjects[CHEST].first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultSpine2, _validTrackedObjects[CHEST].second);
} else {
+ qDebug() << "Puck Calibration: " << configToString(_config) << " Config Failed: Could not meet the minimal # of pucks";
uncalibrate();
return;
}
_calibrated = true;
+ qDebug() << "PuckCalibration: " << configToString(_config) << " Configuration Successful";
}
void ViveControllerManager::InputDevice::uncalibrate() {
@@ -618,9 +625,9 @@ void ViveControllerManager::InputDevice::saveSettings() const {
settings.endGroup();
}
-QString ViveControllerManager::InputDevice::configToString() {
+QString ViveControllerManager::InputDevice::configToString(Config config) {
QString currentConfig;
- switch (_preferedConfig) {
+ switch (config) {
case Config::Auto:
currentConfig = "Auto";
break;
@@ -658,7 +665,7 @@ void ViveControllerManager::InputDevice::createPreferences() {
static const QString VIVE_PUCKS_CONFIG = "Vive Pucks Configuration";
{
- auto getter = [this]()->QString { return configToString(); };
+ auto getter = [this]()->QString { return configToString(_preferedConfig); };
auto setter = [this](const QString& value) { setConfigFromString(value); saveSettings(); };
auto preference = new ComboBoxPreference(VIVE_PUCKS_CONFIG, "Configuration", getter, setter);
QStringList list = (QStringList() << "Auto" << "Feet" << "FeetAndHips" << "FeetHipsAndChest");
diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h
index c815506770..d7ab77ddbc 100644
--- a/plugins/openvr/src/ViveControllerManager.h
+++ b/plugins/openvr/src/ViveControllerManager.h
@@ -129,7 +129,7 @@ private:
bool _timeTilCalibrationSet { false };
mutable std::recursive_mutex _lock;
- QString configToString();
+ QString configToString(Config config);
void setConfigFromString(const QString& value);
void loadSettings();
void saveSettings() const;
diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js
index a8afad2e1c..37a334bd70 100644
--- a/scripts/system/makeUserConnection.js
+++ b/scripts/system/makeUserConnection.js
@@ -198,7 +198,7 @@
}
var animationData = {};
- function updateAnimationData() {
+ function updateAnimationData(verticalOffset) {
// all we are doing here is moving the right hand to a spot
// that is in front of and a bit above the hips. Basing how
// far in front as scaling with the avatar's height (say hips
@@ -209,6 +209,9 @@
offset = 0.8 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
}
animationData.rightHandPosition = Vec3.multiply(offset, {x: -0.25, y: 0.8, z: 1.3});
+ if (verticalOffset) {
+ animationData.rightHandPosition.y += verticalOffset;
+ }
animationData.rightHandRotation = Quat.fromPitchYawRollDegrees(90, 0, 90);
}
function shakeHandsAnimation() {
@@ -347,7 +350,32 @@
}
return false;
}
-
+ function findNearestAvatar() {
+ // We only look some max distance away (much larger than the handshake distance, but still...)
+ var minDistance = MAX_AVATAR_DISTANCE * 20;
+ var closestAvatar;
+ AvatarList.getAvatarIdentifiers().forEach(function (id) {
+ var avatar = AvatarList.getAvatar(id);
+ if (avatar && avatar.sessionUUID != MyAvatar.sessionUUID) {
+ var currentDistance = Vec3.distance(avatar.position, MyAvatar.position);
+ if (minDistance > currentDistance) {
+ minDistance = currentDistance;
+ closestAvatar = avatar;
+ }
+ }
+ });
+ return closestAvatar;
+ }
+ function adjustAnimationHeight() {
+ var avatar = findNearestAvatar();
+ if (avatar) {
+ var myHeadIndex = MyAvatar.getJointIndex("Head");
+ var otherHeadIndex = avatar.getJointIndex("Head");
+ var diff = (avatar.getJointPosition(otherHeadIndex).y - MyAvatar.getJointPosition(myHeadIndex).y) / 2;
+ print("head height difference: " + diff);
+ updateAnimationData(diff);
+ }
+ }
function findNearestWaitingAvatar() {
var handPosition = getHandPosition(MyAvatar, currentHandJointIndex);
var minDistance = MAX_AVATAR_DISTANCE;
@@ -436,6 +464,10 @@
handStringMessageSend({
key: "waiting",
});
+ // potentially adjust height of handshake
+ if (fromKeyboard) {
+ adjustAnimationHeight();
+ }
lookForWaitingAvatar();
}
}
diff --git a/unpublishedScripts/marketplace/tablet-raiseHand/tablet-raiseHand.js b/unpublishedScripts/marketplace/tablet-raiseHand/tablet-raiseHand.js
new file mode 100644
index 0000000000..f7702053a4
--- /dev/null
+++ b/unpublishedScripts/marketplace/tablet-raiseHand/tablet-raiseHand.js
@@ -0,0 +1,102 @@
+"use strict";
+//
+// tablet-raiseHand.js
+//
+// client script that creates a tablet button to raise hand
+//
+// Created by Triplelexx on 17/04/22
+// Copyright 2017 High Fidelity, Inc.
+//
+// Hand icons adapted from https://linearicons.com, created by Perxis https://perxis.com CC BY-SA 4.0 license.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+(function() { // BEGIN LOCAL_SCOPE
+ var BUTTON_NAME = "RAISE\nHAND";
+ var USERCONNECTION_MESSAGE_CHANNEL = "io.highfidelity.makeUserConnection";
+ var DEBUG_PREFIX = "TABLET RAISE HAND: ";
+ var isRaiseHandButtonActive = false;
+ var animHandlerId;
+
+ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+ var button = tablet.addButton({
+ text: BUTTON_NAME,
+ icon: "icons/tablet-icons/raise-hand-i.svg",
+ activeIcon: "icons/tablet-icons/raise-hand-a.svg"
+ });
+
+ function onClicked() {
+ isRaiseHandButtonActive = !isRaiseHandButtonActive;
+ button.editProperties({ isActive: isRaiseHandButtonActive });
+ if (isRaiseHandButtonActive) {
+ removeAnimation();
+ animHandlerId = MyAvatar.addAnimationStateHandler(raiseHandAnimation, []);
+ Messages.subscribe(USERCONNECTION_MESSAGE_CHANNEL);
+ Messages.messageReceived.connect(messageHandler);
+ } else {
+ removeAnimation();
+ Messages.unsubscribe(USERCONNECTION_MESSAGE_CHANNEL);
+ Messages.messageReceived.disconnect(messageHandler);
+ }
+ }
+
+ function removeAnimation() {
+ if (animHandlerId) {
+ animHandlerId = MyAvatar.removeAnimationStateHandler(animHandlerId);
+ }
+ }
+
+ function raiseHandAnimation(animationProperties) {
+ // all we are doing here is moving the right hand to a spot that is above the hips.
+ var headIndex = MyAvatar.getJointIndex("Head");
+ var offset = 0.0;
+ var result = {};
+ if (headIndex) {
+ offset = 0.85 * MyAvatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y;
+ }
+ var handPos = Vec3.multiply(offset, { x: -0.7, y: 1.25, z: 0.25 });
+ result.rightHandPosition = handPos;
+ result.rightHandRotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
+ return result;
+ }
+
+ function messageHandler(channel, messageString, senderID) {
+ if (channel !== USERCONNECTION_MESSAGE_CHANNEL && senderID !== MyAvatar.sessionUUID) {
+ return;
+ }
+ var message = {};
+ try {
+ message = JSON.parse(messageString);
+ } catch (e) {
+ print(DEBUG_PREFIX + "messageHandler error: " + e);
+ }
+ switch (message.key) {
+ case "waiting":
+ case "connecting":
+ case "connectionAck":
+ case "connectionRequest":
+ case "done":
+ removeAnimation();
+ if (isRaiseHandButtonActive) {
+ isRaiseHandButtonActive = false;
+ button.editProperties({ isActive: isRaiseHandButtonActive });
+ }
+ break;
+ default:
+ print(DEBUG_PREFIX + "messageHandler unknown message: " + message);
+ break;
+ }
+ }
+
+ button.clicked.connect(onClicked);
+
+ Script.scriptEnding.connect(function() {
+ Messages.unsubscribe(USERCONNECTION_MESSAGE_CHANNEL);
+ Messages.messageReceived.disconnect(messageHandler);
+ button.clicked.disconnect(onClicked);
+ tablet.removeButton(button);
+ removeAnimation();
+ });
+}()); // END LOCAL_SCOPE