diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 7e9042e609..bd656ceb09 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -9,8 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include +#include "AssignmentClientApp.h" + +#include +#include +#include +#include #include #include @@ -20,10 +24,6 @@ #include "Assignment.h" #include "AssignmentClient.h" #include "AssignmentClientMonitor.h" -#include "AssignmentClientApp.h" -#include -#include - AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : QCoreApplication(argc, argv) @@ -87,6 +87,9 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : const QCommandLineOption logDirectoryOption(ASSIGNMENT_LOG_DIRECTORY, "directory to store logs", "log-directory"); parser.addOption(logDirectoryOption); + const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid"); + parser.addOption(parentPIDOption); + if (!parser.parse(QCoreApplication::arguments())) { qCritical() << parser.errorText() << endl; parser.showHelp(); @@ -203,6 +206,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : } } + if (parser.isSet(parentPIDOption)) { + bool ok = false; + int parentPID = parser.value(parentPIDOption).toInt(&ok); + + if (ok) { + qDebug() << "Parent process PID is" << parentPID; + watchParentProcess(parentPID); + } + } + QThread::currentThread()->setObjectName("main thread"); DependencyManager::registerInheritance(); diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 1ee876ceea..070034d54b 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -131,7 +131,6 @@ void AssignmentClientMonitor::aboutToQuit() { void AssignmentClientMonitor::spawnChildClient() { QProcess* assignmentClient = new QProcess(this); - // unparse the parts of the command-line that the child cares about QStringList _childArguments; if (_assignmentPool != "") { @@ -160,6 +159,9 @@ void AssignmentClientMonitor::spawnChildClient() { _childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION); _childArguments.append(QString::number(DependencyManager::get()->getLocalSockAddr().getPort())); + _childArguments.append("--" + PARENT_PID_OPTION); + _childArguments.append(QString::number(QCoreApplication::applicationPid())); + QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index f218daed03..b4aae40c10 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -285,6 +285,13 @@ void AvatarMixer::start() { // is guaranteed to not be accessed by other thread void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); + + // there is no need to manage identity data we haven't received yet + // so bail early if we've never received an identity packet for this avatar + if (!nodeData || !nodeData->getAvatar().hasProcessedFirstIdentity()) { + return; + } + bool sendIdentity = false; if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) { AvatarData& avatar = nodeData->getAvatar(); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 4ff447a95a..7e37f583ff 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -320,14 +320,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) ++numOtherAvatars; const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); // If the time that the mixer sent AVATAR DATA about Avatar B to Avatar A is BEFORE OR EQUAL TO // the time that Avatar B flagged an IDENTITY DATA change, send IDENTITY DATA about Avatar B to Avatar A. - if (nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) { + if (otherAvatar->hasProcessedFirstIdentity() + && nodeData->getLastBroadcastTime(otherNode->getUUID()) <= otherNodeData->getIdentityChangeTimestamp()) { identityBytesSent += sendIdentityPacket(otherNodeData, node); + + // remember the last time we sent identity details about this other node to the receiver + nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); } - const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); // determine if avatar is in view, to determine how much data to include... @@ -400,9 +404,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // set the last sent sequence number for this sender on the receiver nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), otherNodeData->getLastReceivedSequenceNumber()); - - // remember the last time we sent details about this other node to the receiver - nodeData->setLastBroadcastTime(otherNode->getUUID(), start); } } diff --git a/cmake/macros/TargetLeapMotion.cmake b/cmake/macros/TargetLeapMotion.cmake new file mode 100644 index 0000000000..674ec8f62d --- /dev/null +++ b/cmake/macros/TargetLeapMotion.cmake @@ -0,0 +1,12 @@ +# +# Created by David Rowe on 16 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 +# + +macro(TARGET_LEAPMOTION) + target_include_directories(${TARGET_NAME} PRIVATE ${LEAPMOTION_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${LEAPMOTION_LIBRARIES}) +endmacro() diff --git a/domain-server/src/DomainMetadata.cpp b/domain-server/src/DomainMetadata.cpp index c19cefa397..eee5673af3 100644 --- a/domain-server/src/DomainMetadata.cpp +++ b/domain-server/src/DomainMetadata.cpp @@ -171,7 +171,7 @@ void DomainMetadata::maybeUpdateUsers() { if (linkedData) { auto nodeData = static_cast(linkedData); - if (!nodeData->wasAssigned()) { + if (!nodeData->wasAssigned() && node->getType() == NodeType::Agent) { ++numConnected; if (nodeData->getUsername().isEmpty()) { diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8e3d04897b..095613a473 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -221,6 +221,8 @@ void DomainServer::parseCommandLine() { const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option"); parser.addOption(masterConfigOption); + const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid"); + parser.addOption(parentPIDOption); if (!parser.parse(QCoreApplication::arguments())) { qWarning() << parser.errorText() << endl; @@ -249,6 +251,17 @@ void DomainServer::parseCommandLine() { _overrideDomainID = true; qDebug() << "domain-server ID is" << _overridingDomainID; } + + + if (parser.isSet(parentPIDOption)) { + bool ok = false; + int parentPID = parser.value(parentPIDOption).toInt(&ok); + + if (ok) { + qDebug() << "Parent process PID is" << parentPID; + watchParentProcess(parentPID); + } + } } DomainServer::~DomainServer() { diff --git a/interface/external/leapmotion/readme.txt b/interface/external/leapmotion/readme.txt index 51a65caf22..97502d694c 100644 --- a/interface/external/leapmotion/readme.txt +++ b/interface/external/leapmotion/readme.txt @@ -10,7 +10,7 @@ Interface has been tested with SDK versions: 1. Copy the LeapSDK folders from the LeapDeveloperKit installation directory (Lib, Include) into the interface/externals/leapmotion folder. This readme.txt should be there as well. - The files neeeded in the folders are: + The files needed in the folders are: include/ - Leap.h @@ -21,8 +21,8 @@ Interface has been tested with SDK versions: x86/ - Leap.dll - Leap.lib - - mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed) - - mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistriuable installed) + - mscvcp120.dll (optional if you already have the Msdev 2012 SDK redistributable installed) + - mscvcr120.dll (optional if you already have the Msdev 2012 SDK redistributable installed) - lipLeap.dylib libc++/ -libLeap.dylib @@ -30,4 +30,4 @@ Interface has been tested with SDK versions: 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 'leapmotion' that contains the 2 folders mentioned above (Include, Lib). -2. Clear your build directory, run cmake and build, and you should be all set. \ No newline at end of file +2. Clear your build directory, run cmake and build, and you should be all set. diff --git a/interface/resources/controllers/leapmotion.json b/interface/resources/controllers/leapmotion.json new file mode 100644 index 0000000000..25cb575946 --- /dev/null +++ b/interface/resources/controllers/leapmotion.json @@ -0,0 +1,48 @@ +{ + "name": "Leap Motion to Standard", + "channels": [ + { "from": "LeapMotion.LeftHand", "to": "Standard.LeftHand" }, + { "from": "LeapMotion.LeftHandThumb1", "to": "Standard.LeftHandThumb1"}, + { "from": "LeapMotion.LeftHandThumb2", "to": "Standard.LeftHandThumb2"}, + { "from": "LeapMotion.LeftHandThumb3", "to": "Standard.LeftHandThumb3"}, + { "from": "LeapMotion.LeftHandThumb4", "to": "Standard.LeftHandThumb4"}, + { "from": "LeapMotion.LeftHandIndex1", "to": "Standard.LeftHandIndex1"}, + { "from": "LeapMotion.LeftHandIndex2", "to": "Standard.LeftHandIndex2"}, + { "from": "LeapMotion.LeftHandIndex3", "to": "Standard.LeftHandIndex3"}, + { "from": "LeapMotion.LeftHandIndex4", "to": "Standard.LeftHandIndex4"}, + { "from": "LeapMotion.LeftHandMiddle1", "to": "Standard.LeftHandMiddle1"}, + { "from": "LeapMotion.LeftHandMiddle2", "to": "Standard.LeftHandMiddle2"}, + { "from": "LeapMotion.LeftHandMiddle3", "to": "Standard.LeftHandMiddle3"}, + { "from": "LeapMotion.LeftHandMiddle4", "to": "Standard.LeftHandMiddle4"}, + { "from": "LeapMotion.LeftHandRing1", "to": "Standard.LeftHandRing1"}, + { "from": "LeapMotion.LeftHandRing2", "to": "Standard.LeftHandRing2"}, + { "from": "LeapMotion.LeftHandRing3", "to": "Standard.LeftHandRing3"}, + { "from": "LeapMotion.LeftHandRing4", "to": "Standard.LeftHandRing4"}, + { "from": "LeapMotion.LeftHandPinky1", "to": "Standard.LeftHandPinky1"}, + { "from": "LeapMotion.LeftHandPinky2", "to": "Standard.LeftHandPinky2"}, + { "from": "LeapMotion.LeftHandPinky3", "to": "Standard.LeftHandPinky3"}, + { "from": "LeapMotion.LeftHandPinky4", "to": "Standard.LeftHandPinky4"}, + + { "from": "LeapMotion.RightHand", "to": "Standard.RightHand" }, + { "from": "LeapMotion.RightHandThumb1", "to": "Standard.RightHandThumb1"}, + { "from": "LeapMotion.RightHandThumb2", "to": "Standard.RightHandThumb2"}, + { "from": "LeapMotion.RightHandThumb3", "to": "Standard.RightHandThumb3"}, + { "from": "LeapMotion.RightHandThumb4", "to": "Standard.RightHandThumb4"}, + { "from": "LeapMotion.RightHandIndex1", "to": "Standard.RightHandIndex1"}, + { "from": "LeapMotion.RightHandIndex2", "to": "Standard.RightHandIndex2"}, + { "from": "LeapMotion.RightHandIndex3", "to": "Standard.RightHandIndex3"}, + { "from": "LeapMotion.RightHandIndex4", "to": "Standard.RightHandIndex4"}, + { "from": "LeapMotion.RightHandMiddle1", "to": "Standard.RightHandMiddle1"}, + { "from": "LeapMotion.RightHandMiddle2", "to": "Standard.RightHandMiddle2"}, + { "from": "LeapMotion.RightHandMiddle3", "to": "Standard.RightHandMiddle3"}, + { "from": "LeapMotion.RightHandMiddle4", "to": "Standard.RightHandMiddle4"}, + { "from": "LeapMotion.RightHandRing1", "to": "Standard.RightHandRing1"}, + { "from": "LeapMotion.RightHandRing2", "to": "Standard.RightHandRing2"}, + { "from": "LeapMotion.RightHandRing3", "to": "Standard.RightHandRing3"}, + { "from": "LeapMotion.RightHandRing4", "to": "Standard.RightHandRing4"}, + { "from": "LeapMotion.RightHandPinky1", "to": "Standard.RightHandPinky1"}, + { "from": "LeapMotion.RightHandPinky2", "to": "Standard.RightHandPinky2"}, + { "from": "LeapMotion.RightHandPinky3", "to": "Standard.RightHandPinky3"}, + { "from": "LeapMotion.RightHandPinky4", "to": "Standard.RightHandPinky4"} + ] +} diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 75b4821118..166f1a6869 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -58,7 +58,48 @@ { "from": "Standard.RT", "to": "Actions.RightHandClick" }, { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, + { "from": "Standard.LeftHandThumb1", "to": "Actions.LeftHandThumb1"}, + { "from": "Standard.LeftHandThumb2", "to": "Actions.LeftHandThumb2"}, + { "from": "Standard.LeftHandThumb3", "to": "Actions.LeftHandThumb3"}, + { "from": "Standard.LeftHandThumb4", "to": "Actions.LeftHandThumb4"}, + { "from": "Standard.LeftHandIndex1", "to": "Actions.LeftHandIndex1"}, + { "from": "Standard.LeftHandIndex2", "to": "Actions.LeftHandIndex2"}, + { "from": "Standard.LeftHandIndex3", "to": "Actions.LeftHandIndex3"}, + { "from": "Standard.LeftHandIndex4", "to": "Actions.LeftHandIndex4"}, + { "from": "Standard.LeftHandMiddle1", "to": "Actions.LeftHandMiddle1"}, + { "from": "Standard.LeftHandMiddle2", "to": "Actions.LeftHandMiddle2"}, + { "from": "Standard.LeftHandMiddle3", "to": "Actions.LeftHandMiddle3"}, + { "from": "Standard.LeftHandMiddle4", "to": "Actions.LeftHandMiddle4"}, + { "from": "Standard.LeftHandRing1", "to": "Actions.LeftHandRing1"}, + { "from": "Standard.LeftHandRing2", "to": "Actions.LeftHandRing2"}, + { "from": "Standard.LeftHandRing3", "to": "Actions.LeftHandRing3"}, + { "from": "Standard.LeftHandRing4", "to": "Actions.LeftHandRing4"}, + { "from": "Standard.LeftHandPinky1", "to": "Actions.LeftHandPinky1"}, + { "from": "Standard.LeftHandPinky2", "to": "Actions.LeftHandPinky2"}, + { "from": "Standard.LeftHandPinky3", "to": "Actions.LeftHandPinky3"}, + { "from": "Standard.LeftHandPinky4", "to": "Actions.LeftHandPinky4"}, + { "from": "Standard.RightHand", "to": "Actions.RightHand" }, + { "from": "Standard.RightHandThumb1", "to": "Actions.RightHandThumb1"}, + { "from": "Standard.RightHandThumb2", "to": "Actions.RightHandThumb2"}, + { "from": "Standard.RightHandThumb3", "to": "Actions.RightHandThumb3"}, + { "from": "Standard.RightHandThumb4", "to": "Actions.RightHandThumb4"}, + { "from": "Standard.RightHandIndex1", "to": "Actions.RightHandIndex1"}, + { "from": "Standard.RightHandIndex2", "to": "Actions.RightHandIndex2"}, + { "from": "Standard.RightHandIndex3", "to": "Actions.RightHandIndex3"}, + { "from": "Standard.RightHandIndex4", "to": "Actions.RightHandIndex4"}, + { "from": "Standard.RightHandMiddle1", "to": "Actions.RightHandMiddle1"}, + { "from": "Standard.RightHandMiddle2", "to": "Actions.RightHandMiddle2"}, + { "from": "Standard.RightHandMiddle3", "to": "Actions.RightHandMiddle3"}, + { "from": "Standard.RightHandMiddle4", "to": "Actions.RightHandMiddle4"}, + { "from": "Standard.RightHandRing1", "to": "Actions.RightHandRing1"}, + { "from": "Standard.RightHandRing2", "to": "Actions.RightHandRing2"}, + { "from": "Standard.RightHandRing3", "to": "Actions.RightHandRing3"}, + { "from": "Standard.RightHandRing4", "to": "Actions.RightHandRing4"}, + { "from": "Standard.RightHandPinky1", "to": "Actions.RightHandPinky1"}, + { "from": "Standard.RightHandPinky2", "to": "Actions.RightHandPinky2"}, + { "from": "Standard.RightHandPinky3", "to": "Actions.RightHandPinky3"}, + { "from": "Standard.RightHandPinky4", "to": "Actions.RightHandPinky4"}, { "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" }, { "from": "Standard.RightFoot", "to": "Actions.RightFoot" }, diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 0886a25949..106e067968 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -34,6 +34,8 @@ ModalWindow { HifiConstants { id: hifi } + property var filesModel: ListModel { } + Settings { category: "FileDialog" property alias width: root.width @@ -253,7 +255,9 @@ ModalWindow { } currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); - currentSelectionIsFolder = fileTableView.model.isFolder(row); + currentSelectionIsFolder = fileTableView.model !== filesModel ? + fileTableView.model.isFolder(row) : + fileTableModel.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); } else { @@ -331,7 +335,12 @@ ModalWindow { } } - ListModel { + Component { + id: filesModelBuilder + ListModel { } + } + + QtObject { id: fileTableModel // FolderListModel has a couple of problems: @@ -383,7 +392,11 @@ ModalWindow { if (row === -1) { return false; } - return get(row).fileIsDir; + return filesModel.get(row).fileIsDir; + } + + function get(row) { + return filesModel.get(row) } function update() { @@ -401,7 +414,7 @@ ModalWindow { rows = 0, i; - clear(); + var newFilesModel = filesModelBuilder.createObject(root); comparisonFunction = sortOrder === Qt.AscendingOrder ? function(a, b) { return a < b; } @@ -423,7 +436,7 @@ ModalWindow { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, get(middle)[sortField])) { + if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -432,7 +445,7 @@ ModalWindow { } } - insert(lower, { + newFilesModel.insert(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -443,6 +456,7 @@ ModalWindow { rows++; } + filesModel = newFilesModel; d.clearSelection(); } @@ -469,7 +483,7 @@ ModalWindow { sortIndicatorOrder: Qt.AscendingOrder sortIndicatorVisible: true - model: fileTableModel + model: filesModel function updateSort() { model.sortOrder = sortIndicatorOrder; @@ -561,11 +575,12 @@ ModalWindow { } function navigateToCurrentRow() { + var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel var row = fileTableView.currentRow - var isFolder = model.isFolder(row); - var file = model.get(row).filePath; + var isFolder = currentModel.isFolder(row); + var file = currentModel.get(row).filePath; if (isFolder) { - fileTableView.model.folder = helper.pathToUrl(file); + currentModel.folder = helper.pathToUrl(file); } else { okAction.trigger(); } @@ -580,7 +595,8 @@ ModalWindow { var newPrefix = prefix + event.text.toLowerCase(); var matchedIndex = -1; for (var i = 0; i < model.count; ++i) { - var name = model.get(i).fileName.toLowerCase(); + var name = model !== filesModel ? model.get(i).fileName.toLowerCase() : + filesModel.get(i).fileName.toLowerCase(); if (0 === name.indexOf(newPrefix)) { matchedIndex = i; break; diff --git a/interface/resources/qml/dialogs/TabletFileDialog.qml b/interface/resources/qml/dialogs/TabletFileDialog.qml index 5e33663436..9e1d0a9f5a 100644 --- a/interface/resources/qml/dialogs/TabletFileDialog.qml +++ b/interface/resources/qml/dialogs/TabletFileDialog.qml @@ -25,11 +25,14 @@ import "fileDialog" //FIXME implement shortcuts for favorite location TabletModalWindow { id: root + anchors.fill: parent width: parent.width height: parent.height HifiConstants { id: hifi } + property var filesModel: ListModel { } + Settings { category: "FileDialog" property alias width: root.width @@ -250,7 +253,9 @@ TabletModalWindow { } currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath); - currentSelectionIsFolder = fileTableView.model.isFolder(row); + currentSelectionIsFolder = fileTableView.model !== filesModel ? + fileTableView.model.isFolder(row) : + fileTableModel.isFolder(row); if (root.selectDirectory || !currentSelectionIsFolder) { currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl)); } else { @@ -288,7 +293,7 @@ TabletModalWindow { } onFolderChanged: { - fileTableModel.update(); // Update once the data from the folder change is available. + fileTableModel.update() } function getItem(index, field) { @@ -328,7 +333,12 @@ TabletModalWindow { } } - ListModel { + Component { + id: filesModelBuilder + ListModel { } + } + + QtObject { id: fileTableModel // FolderListModel has a couple of problems: @@ -359,17 +369,16 @@ TabletModalWindow { } onFolderChanged: { + if (folder === rootFolder) { model = driveListModel; helper.monitorDirectory(""); update(); } else { var needsUpdate = model === driveListModel && folder === folderListModel.folder; - model = folderListModel; folderListModel.folder = folder; helper.monitorDirectory(helper.urlToPath(folder)); - if (needsUpdate) { update(); } @@ -380,7 +389,11 @@ TabletModalWindow { if (row === -1) { return false; } - return get(row).fileIsDir; + return filesModel.get(row).fileIsDir; + } + + function get(row) { + return filesModel.get(row) } function update() { @@ -398,7 +411,7 @@ TabletModalWindow { rows = 0, i; - clear(); + var newFilesModel = filesModelBuilder.createObject(root); comparisonFunction = sortOrder === Qt.AscendingOrder ? function(a, b) { return a < b; } @@ -420,7 +433,7 @@ TabletModalWindow { while (lower < upper) { middle = Math.floor((lower + upper) / 2); var lessThan; - if (comparisonFunction(sortValue, get(middle)[sortField])) { + if (comparisonFunction(sortValue, newFilesModel.get(middle)[sortField])) { lessThan = true; upper = middle; } else { @@ -429,7 +442,7 @@ TabletModalWindow { } } - insert(lower, { + newFilesModel.insert(lower, { fileName: fileName, fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")), fileSize: model.getItem(i, "fileSize"), @@ -440,6 +453,7 @@ TabletModalWindow { rows++; } + filesModel = newFilesModel; d.clearSelection(); } @@ -467,7 +481,7 @@ TabletModalWindow { sortIndicatorOrder: Qt.AscendingOrder sortIndicatorVisible: true - model: fileTableModel + model: filesModel function updateSort() { model.sortOrder = sortIndicatorOrder; @@ -559,11 +573,12 @@ TabletModalWindow { } function navigateToCurrentRow() { + var currentModel = fileTableView.model !== filesModel ? fileTableView.model : fileTableModel var row = fileTableView.currentRow - var isFolder = model.isFolder(row); - var file = model.get(row).filePath; + var isFolder = currentModel.isFolder(row); + var file = currentModel.get(row).filePath; if (isFolder) { - fileTableView.model.folder = helper.pathToUrl(file); + currentModel.folder = helper.pathToUrl(file); } else { okAction.trigger(); } @@ -578,7 +593,8 @@ TabletModalWindow { var newPrefix = prefix + event.text.toLowerCase(); var matchedIndex = -1; for (var i = 0; i < model.count; ++i) { - var name = model.get(i).fileName.toLowerCase(); + var name = model !== filesModel ? model.get(i).fileName.toLowerCase() : + filesModel.get(i).fileName.toLowerCase(); if (0 === name.indexOf(newPrefix)) { matchedIndex = i; break; diff --git a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml index 44cae95696..cabc09e49b 100644 --- a/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/GeneralPreferencesDialog.qml @@ -17,7 +17,7 @@ PreferencesDialog { id: root objectName: "GeneralPreferencesDialog" title: "General Settings" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Leap Motion"] property var settings: Settings { category: root.objectName property alias x: root.x diff --git a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml index fe043f6ac7..18e7898dd0 100644 --- a/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletGeneralPreferences.qml @@ -32,6 +32,6 @@ StackView { TabletPreferencesDialog { id: root objectName: "TabletGeneralPreferences" - showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration"] + showCategories: ["UI", "Snapshots", "Scripts", "Privacy", "Octree", "HMD", "Sixense Controllers", "Perception Neuron", "Kinect", "Vive Pucks Configuration", "Leap Motion"] } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 75bcee0703..d0845131f6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -145,7 +145,6 @@ #include "avatar/MyHead.h" #include "CrashHandler.h" #include "devices/DdeFaceTracker.h" -#include "devices/Leapmotion.h" #include "DiscoverabilityManager.h" #include "GLCanvas.h" #include "InterfaceDynamicFactory.h" @@ -1886,8 +1885,6 @@ Application::~Application() { // remove the NodeList from the DependencyManager DependencyManager::destroy(); - Leapmotion::destroy(); - if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { steamClient->shutdown(); } @@ -4055,8 +4052,6 @@ void Application::init() { qCDebug(interfaceapp) << "Loaded settings"; - Leapmotion::init(); - // fire off an immediate domain-server check in now that settings are loaded DependencyManager::get()->sendDomainServerCheckIn(); @@ -4520,7 +4515,6 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("devices"); - DeviceTracker::updateAll(); FaceTracker* tracker = getSelectedFaceTracker(); if (tracker && Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking) != tracker->isMuted()) { @@ -4589,8 +4583,6 @@ void Application::update(float deltaTime) { keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData); } - _controllerScriptingInterface->updateInputControllers(); - // 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(); @@ -4615,6 +4607,31 @@ void Application::update(float deltaTime) { auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix; myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix)); + // If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if + // fingers are not being controlled, finger joints are not updated in MySkeletonModel. + // Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint. + MyAvatar::FingerPosesMap leftHandFingerPoses; + if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0 + || userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) { + for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) { + leftHandFingerPoses[i] = { + userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix), + userInputMapper->getActionName((controller::Action)i) + }; + } + } + MyAvatar::FingerPosesMap rightHandFingerPoses; + if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0 + || userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) { + for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) { + rightHandFingerPoses[i] = { + userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix), + userInputMapper->getActionName((controller::Action)i) + }; + } + } + myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses); + controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT); controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT); myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix)); diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index db2a240b92..73192b0bef 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,8 @@ #include "AvatarBookmarks.h" #include "InterfaceLogging.h" +#include "QVariantGLM.h" + #include AvatarBookmarks::AvatarBookmarks() { @@ -58,16 +61,48 @@ void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark); QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection); - Bookmarks::setupMenus(menubar, menu); + for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) { + addBookmarkToMenu(menubar, it.key(), it.value()); + } + Bookmarks::sortActions(menubar, _bookmarksMenu); } void AvatarBookmarks::changeToBookmarkedAvatar() { QAction* action = qobject_cast(sender()); - const QString& address = action->data().toString(); - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->useFullAvatarURL(address); + + + + if (action->data().type() == QVariant::String) { + // TODO: Phase this out eventually. + // Legacy avatar bookmark. + + myAvatar->useFullAvatarURL(action->data().toString()); + qCDebug(interfaceapp) << " Using Legacy V1 Avatar Bookmark "; + } else { + + const QMap bookmark = action->data().toMap(); + // Not magic value. This is the current made version, and if it changes this interpreter should be updated to + // handle the new one separately. + // This is where the avatar bookmark entry is parsed. If adding new Value, make sure to have backward compatability with previous + if (bookmark.value(ENTRY_VERSION) == 3) { + const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); + myAvatar->useFullAvatarURL(avatarUrl); + qCDebug(interfaceapp) << "Avatar On " << avatarUrl; + const QList& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); + + qCDebug(interfaceapp) << "Attach " << attachments; + myAvatar->setAttachmentsVariant(attachments); + + const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); + myAvatar->setAvatarScale(qScale); + + } else { + qCDebug(interfaceapp) << " Bookmark entry does not match client version, make sure client has a handler for the new AvatarBookmark"; + } + } + } void AvatarBookmarks::addBookmark() { @@ -83,13 +118,23 @@ void AvatarBookmarks::addBookmark() { } auto myAvatar = DependencyManager::get()->getMyAvatar(); - const QString& bookmarkAddress = myAvatar->getSkeletonModelURL().toString(); - Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); + + const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString(); + const QVariant& avatarScale = myAvatar->getAvatarScale(); + + // If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION + QVariantMap *bookmark = new QVariantMap; + bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); + bookmark->insert(ENTRY_AVATAR_URL, avatarUrl); + bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale); + bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); + + Bookmarks::addBookmarkToFile(bookmarkName, *bookmark); } -void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) { +void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) { QAction* changeAction = _bookmarksMenu->newAction(); - changeAction->setData(address); + changeAction->setData(bookmark); connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar())); if (!_isMenuSorted) { menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole); diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h index dc5a0aee6e..0529eeb516 100644 --- a/interface/src/AvatarBookmarks.h +++ b/interface/src/AvatarBookmarks.h @@ -21,18 +21,23 @@ class AvatarBookmarks: public Bookmarks, public Dependency { public: AvatarBookmarks(); - void setupMenus(Menu* menubar, MenuWrapper* menu) override; public slots: void addBookmark(); protected: - void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override; - void readFromFile(); + void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) override; + void readFromFile() override; private: const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; + const QString ENTRY_AVATAR_URL = "avatarUrl"; + const QString ENTRY_AVATAR_ATTACHMENTS = "attachments"; + const QString ENTRY_AVATAR_SCALE = "avatarScale"; + const QString ENTRY_VERSION = "version"; + + const int AVATAR_BOOKMARK_VERSION = 3; private slots: void changeToBookmarkedAvatar(); diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 4cbc17f8ce..0bd6b01128 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -28,19 +28,6 @@ Bookmarks::Bookmarks() : _isMenuSorted(false) { } - -void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { - // Enable/Disable menus as needed - enableMenuItems(_bookmarks.count() > 0); - - // Load Bookmarks - for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) { - QString bookmarkName = it.key(); - QString bookmarkAddress = it.value().toString(); - addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress); - } -} - void Bookmarks::deleteBookmark() { QStringList bookmarkList; QList menuItems = _bookmarksMenu->actions(); @@ -67,7 +54,7 @@ void Bookmarks::deleteBookmark() { } } -void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress) { +void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark) { Menu* menubar = Menu::getInstance(); if (contains(bookmarkName)) { auto offscreenUi = DependencyManager::get(); @@ -75,7 +62,6 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo "The bookmark name you entered already exists in your list.", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?"); - auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage); if (result != QMessageBox::Yes) { return; @@ -83,19 +69,20 @@ void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bo removeBookmarkFromMenu(menubar, bookmarkName); } - addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress); - insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. + addBookmarkToMenu(menubar, bookmarkName, bookmark); + insert(bookmarkName, bookmark); // Overwrites any item with the same bookmarkName. enableMenuItems(true); } -void Bookmarks::insert(const QString& name, const QString& address) { - _bookmarks.insert(name, address); +void Bookmarks::insert(const QString& name, const QVariant& bookmark) { + _bookmarks.insert(name, bookmark); if (contains(name)) { - qCDebug(interfaceapp) << "Added bookmark:" << name << "," << address; + qCDebug(interfaceapp) << "Added bookmark:" << name; persistToFile(); - } else { - qWarning() << "Couldn't add bookmark: " << name << "," << address; + } + else { + qWarning() << "Couldn't add bookmark: " << name; } } diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index 6c322197cc..dd47a286bf 100644 --- a/interface/src/Bookmarks.h +++ b/interface/src/Bookmarks.h @@ -27,18 +27,20 @@ class Bookmarks: public QObject { public: Bookmarks(); - virtual void setupMenus(Menu* menubar, MenuWrapper* menu); + virtual void setupMenus(Menu* menubar, MenuWrapper* menu) = 0; QString addressForBookmark(const QString& name) const; protected: - virtual void addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress); - virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) = 0; + void addBookmarkToFile(const QString& bookmarkName, const QVariant& bookmark); + virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& bookmark) = 0; void enableMenuItems(bool enabled); - void readFromFile(); - void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name. + virtual void readFromFile(); + void insert(const QString& name, const QVariant& address); // Overwrites any existing entry with same name. void sortActions(Menu* menubar, MenuWrapper* menu); int getMenuItemLocation(QList actions, const QString& name) const; - + + bool contains(const QString& name) const; + QVariantMap _bookmarks; // { name: url, ... } QPointer _bookmarksMenu; QPointer _deleteBookmarksAction; @@ -50,7 +52,6 @@ protected slots: private: void remove(const QString& name); - bool contains(const QString& name) const; static bool sortOrder(QAction* a, QAction* b); void persistToFile(); diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp index b79adcf1b7..eee6cdf3c8 100644 --- a/interface/src/LocationBookmarks.cpp +++ b/interface/src/LocationBookmarks.cpp @@ -41,13 +41,25 @@ void LocationBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark); QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection); - Bookmarks::setupMenus(menubar, menu); + // Legacy Location to Bookmark. + + // Enable/Disable menus as needed + enableMenuItems(_bookmarks.count() > 0); + + // Load Bookmarks + for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) { + QString bookmarkName = it.key(); + QString bookmarkAddress = it.value().toString(); + addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress); + } + Bookmarks::sortActions(menubar, _bookmarksMenu); } void LocationBookmarks::setHomeLocation() { auto addressManager = DependencyManager::get(); QString bookmarkAddress = addressManager->currentAddress().toString(); + Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress); } @@ -74,7 +86,7 @@ void LocationBookmarks::addBookmark() { Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); } -void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) { +void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) { QAction* teleportAction = _bookmarksMenu->newAction(); teleportAction->setData(address); connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); @@ -85,4 +97,4 @@ void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, co menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole); Bookmarks::sortActions(menubar, _bookmarksMenu); } -} +} \ No newline at end of file diff --git a/interface/src/LocationBookmarks.h b/interface/src/LocationBookmarks.h index 1324e96574..6cac56ae04 100644 --- a/interface/src/LocationBookmarks.h +++ b/interface/src/LocationBookmarks.h @@ -29,7 +29,7 @@ public slots: void addBookmark(); protected: - void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override; + void addBookmarkToMenu(Menu* menubar, const QString& name, const QVariant& address) override; private: const QString LOCATIONBOOKMARKS_FILENAME = "bookmarks.json"; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7d4535c681..a746d72d91 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -566,9 +566,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false, avatar.get(), SLOT(setEnableDebugDrawHandControllers(bool))); - MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); - addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); - // Developer > Entities >>> MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cc8e67c8f7..252fbcc142 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -114,7 +114,6 @@ namespace MenuOption { const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IndependentMode = "Independent Mode"; const QString ActionMotorControl = "Enable Default Motor Control"; - const QString LeapMotionOnHMD = "Leap Motion on HMD"; const QString LoadScript = "Open and Run Script File..."; const QString LoadScriptURL = "Open and Run Script from URL..."; const QString LodTools = "LOD Tools"; diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index f6ee8caa61..f59d2fcc7a 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -79,6 +79,7 @@ public: gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.disableContextStereo(); + batch.disableContextViewCorrection(); }); auto srcViewFrustum = args->getViewFrustum(); @@ -112,6 +113,7 @@ public: gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.restoreContextStereo(); + batch.restoreContextViewCorrection(); }); } }; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d676f473e0..5d4d93a02f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1455,6 +1455,19 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const { return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix); } +void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) { + _leftHandFingerPosesInSensorFramceCache.set(left); + _rightHandFingerPosesInSensorFramceCache.set(right); +} + +MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const { + return _leftHandFingerPosesInSensorFramceCache.get(); +} + +MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const { + return _rightHandFingerPosesInSensorFramceCache.get(); +} + void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) { _leftFootControllerPoseInSensorFrameCache.set(left); _rightFootControllerPoseInSensorFrameCache.set(right); @@ -2525,6 +2538,21 @@ bool MyAvatar::getFlyingEnabled() { return _enableFlying; } +// Public interface for targetscale +float MyAvatar::getAvatarScale() { + return getTargetScale(); +} + +void MyAvatar::setAvatarScale(float val) { + + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAvatarScale", Q_ARG(float, val)); + return; + } + + setTargetScale(val); +} + void MyAvatar::setCollisionsEnabled(bool enabled) { if (QThread::currentThread() != thread()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e2ea745ed0..8ad4bf4436 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -366,7 +366,7 @@ public: float getDriveKey(DriveKeys key) const; Q_INVOKABLE float getRawDriveKey(DriveKeys key) const; void relayDriveKeysToCharacterController(); - + Q_INVOKABLE void disableDriveKey(DriveKeys key); Q_INVOKABLE void enableDriveKey(DriveKeys key); Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const; @@ -474,6 +474,11 @@ public: controller::Pose getLeftHandControllerPoseInAvatarFrame() const; controller::Pose getRightHandControllerPoseInAvatarFrame() const; + typedef std::map> FingerPosesMap; + void setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right); + FingerPosesMap getLeftHandFingerControllerPosesInSensorFrame() const; + FingerPosesMap getRightHandFingerControllerPosesInSensorFrame() const; + void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right); controller::Pose getLeftFootControllerPoseInSensorFrame() const; controller::Pose getRightFootControllerPoseInSensorFrame() const; @@ -512,6 +517,9 @@ public: Q_INVOKABLE void setFlyingEnabled(bool enabled); Q_INVOKABLE bool getFlyingEnabled(); + Q_INVOKABLE float getAvatarScale(); + Q_INVOKABLE void setAvatarScale(float scale); + Q_INVOKABLE void setCollisionsEnabled(bool enabled); Q_INVOKABLE bool getCollisionsEnabled(); Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated @@ -788,13 +796,15 @@ private: // These are stored in SENSOR frame ThreadSafeValueCache _leftHandControllerPoseInSensorFrameCache { controller::Pose() }; ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() }; - ThreadSafeValueCache _leftFootControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _rightFootControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _hipsControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _spine2ControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _headControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _leftArmControllerPoseInSensorFrameCache{ controller::Pose() }; - ThreadSafeValueCache _rightArmControllerPoseInSensorFrameCache{ controller::Pose() }; + ThreadSafeValueCache _leftHandFingerPosesInSensorFramceCache { }; + ThreadSafeValueCache _rightHandFingerPosesInSensorFramceCache { }; + ThreadSafeValueCache _leftFootControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _rightFootControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _hipsControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _spine2ControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _headControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _leftArmControllerPoseInSensorFrameCache { controller::Pose() }; + ThreadSafeValueCache _rightArmControllerPoseInSensorFrameCache { controller::Pose() }; bool _hmdLeanRecenterEnabled = true; AnimPose _prePhysicsRoomPose; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 89b6c36c63..97309d9678 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -96,7 +96,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.controllerPoses[Rig::ControllerType_RightArm] = AnimPose::identity; params.controllerActiveFlags[Rig::ControllerType_RightArm] = false; } - + auto avatarLeftArmPose = myAvatar->getLeftArmControllerPoseInAvatarFrame(); if (avatarLeftArmPose.isValid()) { AnimPose pose(avatarLeftArmPose.getRotation(), avatarLeftArmPose.getTranslation()); @@ -174,5 +174,50 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); + + updateFingers(myAvatar->getLeftHandFingerControllerPosesInSensorFrame()); + updateFingers(myAvatar->getRightHandFingerControllerPosesInSensorFrame()); } + +void MySkeletonModel::updateFingers(const MyAvatar::FingerPosesMap& fingerPoses) { + // Assumes that finger poses are kept in order in the poses map. + + if (fingerPoses.size() == 0) { + return; +} + + auto posesMapItr = fingerPoses.begin(); + + bool isLeftHand = posesMapItr->first < (int)controller::Action::RIGHT_HAND_THUMB1; + + MyAvatar* myAvatar = static_cast(_owningAvatar); + auto handPose = isLeftHand + ? myAvatar->getLeftHandControllerPoseInSensorFrame() + : myAvatar->getRightHandControllerPoseInSensorFrame(); + auto handJointRotation = handPose.getRotation(); + + bool isHandValid = handPose.isValid(); + bool isFingerValid = false; + glm::quat previousJointRotation; + + while (posesMapItr != fingerPoses.end()) { + auto jointName = posesMapItr->second.second; + if (isHandValid && jointName.right(1) == "1") { + isFingerValid = posesMapItr->second.first.isValid(); + previousJointRotation = handJointRotation; + } + + if (isHandValid && isFingerValid) { + auto thisJointRotation = posesMapItr->second.first.getRotation(); + const float CONTROLLER_PRIORITY = 2.0f; + _rig.setJointRotation(_rig.indexOfJoint(jointName), true, glm::inverse(previousJointRotation) * thisJointRotation, + CONTROLLER_PRIORITY); + previousJointRotation = thisJointRotation; + } else { + _rig.clearJointAnimationPriority(_rig.indexOfJoint(jointName)); + } + + posesMapItr++; + } +} diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h index 12aba6b545..6867c596af 100644 --- a/interface/src/avatar/MySkeletonModel.h +++ b/interface/src/avatar/MySkeletonModel.h @@ -10,6 +10,7 @@ #define hifi_MySkeletonModel_h #include +#include "MyAvatar.h" /// A skeleton loaded from a model. class MySkeletonModel : public SkeletonModel { @@ -21,6 +22,9 @@ private: public: MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr); void updateRig(float deltaTime, glm::mat4 parentTransform) override; + +private: + void updateFingers(const MyAvatar::FingerPosesMap& fingerPoses); }; #endif // hifi_MySkeletonModel_h diff --git a/interface/src/devices/Leapmotion.cpp b/interface/src/devices/Leapmotion.cpp deleted file mode 100644 index c643042300..0000000000 --- a/interface/src/devices/Leapmotion.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// -// Created by Sam Cake on 6/2/2014 -// 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 "Leapmotion.h" - -#include - -#include "Menu.h" - -const int PALMROOT_NUM_JOINTS = 3; -const int FINGER_NUM_JOINTS = 4; -const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS; - -const DeviceTracker::Name Leapmotion::NAME = "Leapmotion"; - -// find the index of a joint from -// the side: true = right -// the finger & the bone: -// finger in [0..4] : bone in [0..3] a finger phalange -// [-1] up the hand branch : bone in [0..2] <=> [ hand, forearm, arm] -MotionTracker::Index evalJointIndex(bool isRightSide, int finger, int bone) { - - MotionTracker::Index offset = 1 // start after root - + (int(isRightSide) * HAND_NUM_JOINTS) // then offset for side - + PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain - if (finger >= 0) { - // from there go down in the correct finger and bone - return offset + (finger * FINGER_NUM_JOINTS) + bone; - } else { - // or go back up for the correct root bone - return offset - 1 - bone; - } -} - -// static -void Leapmotion::init() { - DeviceTracker* device = DeviceTracker::getDevice(NAME); - - if (!device) { - // create a new Leapmotion and register it - Leapmotion* leap = new Leapmotion(); - DeviceTracker::registerDevice(NAME, leap); - } -} - -// static -void Leapmotion::destroy() { - DeviceTracker::destroyDevice(NAME); -} - -// static -Leapmotion* Leapmotion::getInstance() { - DeviceTracker* device = DeviceTracker::getDevice(NAME); - if (!device) { - // create a new Leapmotion and register it - device = new Leapmotion(); - DeviceTracker::registerDevice(NAME, device); - } - return dynamic_cast< Leapmotion* > (device); -} - -Leapmotion::Leapmotion() : - MotionTracker(), - _active(false) -{ - // Create the Leapmotion joint hierarchy - std::vector< Semantic > sides; - sides.push_back("joint_L_"); - sides.push_back("joint_R_"); - - std::vector< Semantic > rootBones; - rootBones.push_back("elbow"); - rootBones.push_back("wrist"); - rootBones.push_back("hand"); - - std::vector< Semantic > fingers; - fingers.push_back("thumb"); - fingers.push_back("index"); - fingers.push_back("middle"); - fingers.push_back("ring"); - fingers.push_back("pinky"); - - std::vector< Semantic > fingerBones; - fingerBones.push_back("1"); - fingerBones.push_back("2"); - fingerBones.push_back("3"); - fingerBones.push_back("4"); - - std::vector< Index > palms; - for (unsigned int s = 0; s < sides.size(); s++) { - Index rootJoint = 0; - for (unsigned int rb = 0; rb < rootBones.size(); rb++) { - rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint); - } - - // capture the hand index for debug - palms.push_back(rootJoint); - - for (unsigned int f = 0; f < fingers.size(); f++) { - for (unsigned int b = 0; b < fingerBones.size(); b++) { - rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint); - } - } - } - -#ifdef HAVE_LEAPMOTION - if (Menu::getInstance()->isOptionChecked(MenuOption::LeapMotionOnHMD)) { - _controller.setPolicyFlags(Leap::Controller::POLICY_OPTIMIZE_HMD); - } -#endif -} - -Leapmotion::~Leapmotion() { -} - -#ifdef HAVE_LEAPMOTION -glm::quat quatFromLeapBase(float sideSign, const Leap::Matrix& basis) { - - // fix the handness to right and always... - glm::vec3 xAxis = glm::normalize(sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z)); - glm::vec3 yAxis = glm::normalize(glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z)); - glm::vec3 zAxis = glm::normalize(glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z)); - - xAxis = glm::normalize(glm::cross(yAxis, zAxis)); - - glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis))); - return orientation; -} - -glm::vec3 vec3FromLeapVector(const Leap::Vector& vec) { - return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER); -} - -#endif - -void Leapmotion::update() { -#ifdef HAVE_LEAPMOTION - bool wasActive = _active; - _active = _controller.isConnected(); - - if (_active || wasActive) { - // Go through all the joints and increment their counter since last update. - // Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive. - // TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) { - for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) { - (*jointIt).tickNewFrame(); - } - } - - if (!_active) { - return; - } - - // Get the most recent frame and report some basic information - const Leap::Frame frame = _controller.frame(); - static int64_t lastFrameID = -1; - int64_t newFrameID = frame.id(); - - // If too soon then exit - if (lastFrameID >= newFrameID) - return; - - glm::vec3 delta(0.0f); - glm::quat handOri; - if (!frame.hands().isEmpty()) { - for (int handNum = 0; handNum < frame.hands().count(); handNum++) { - - const Leap::Hand hand = frame.hands()[handNum]; - int side = (hand.isRight() ? 1 : -1); - - JointTracker* parentJointTracker = _jointsArray.data(); - - - int rootBranchIndex = -1; - - Leap::Arm arm = hand.arm(); - if (arm.isValid()) { - glm::quat ori = quatFromLeapBase(float(side), arm.basis()); - glm::vec3 pos = vec3FromLeapVector(arm.elbowPosition()); - JointTracker* elbow = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 2)); // 2 is the index of the elbow joint - elbow->editAbsFrame().setTranslation(pos); - elbow->editAbsFrame().setRotation(ori); - elbow->updateLocFromAbsTransform(parentJointTracker); - elbow->activeFrame(); - parentJointTracker = elbow; - - pos = vec3FromLeapVector(arm.wristPosition()); - JointTracker* wrist = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 1)); // 1 is the index of the wrist joint - wrist->editAbsFrame().setTranslation(pos); - wrist->editAbsFrame().setRotation(ori); - wrist->updateLocFromAbsTransform(parentJointTracker); - wrist->activeFrame(); - parentJointTracker = wrist; - } - - JointTracker* palmJoint = NULL; - { - glm::vec3 pos = vec3FromLeapVector(hand.palmPosition()); - glm::quat ori = quatFromLeapBase(float(side), hand.basis()); - - palmJoint = editJointTracker(evalJointIndex((side > 0), rootBranchIndex, 0)); // 0 is the index of the palm joint - palmJoint->editAbsFrame().setTranslation(pos); - palmJoint->editAbsFrame().setRotation(ori); - palmJoint->updateLocFromAbsTransform(parentJointTracker); - palmJoint->activeFrame(); - } - - // Check if the hand has any fingers - const Leap::FingerList fingers = hand.fingers(); - if (!fingers.isEmpty()) { - // For every fingers in the list - for (int i = 0; i < fingers.count(); ++i) { - // Reset the parent joint to the palmJoint for every finger traversal - parentJointTracker = palmJoint; - - // surprisingly, Leap::Finger::Type start at 0 for thumb a until 4 for the pinky - Index fingerIndex = evalJointIndex((side > 0), int(fingers[i].type()), 0); - - // let's update the finger's joints - for (int b = 0; b < FINGER_NUM_JOINTS; b++) { - Leap::Bone::Type type = Leap::Bone::Type(b + Leap::Bone::TYPE_METACARPAL); - Leap::Bone bone = fingers[i].bone(type); - JointTracker* ljointTracker = editJointTracker(fingerIndex + b); - if (bone.isValid()) { - Leap::Vector bp = bone.nextJoint(); - - ljointTracker->editAbsFrame().setTranslation(vec3FromLeapVector(bp)); - ljointTracker->editAbsFrame().setRotation(quatFromLeapBase(float(side), bone.basis())); - ljointTracker->updateLocFromAbsTransform(parentJointTracker); - ljointTracker->activeFrame(); - } - parentJointTracker = ljointTracker; - } - } - } - } - } - - lastFrameID = newFrameID; -#endif -} diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h deleted file mode 100644 index a119821846..0000000000 --- a/interface/src/devices/Leapmotion.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Created by Sam Cake on 6/2/2014 -// 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_Leapmotion_h -#define hifi_Leapmotion_h - -#include - -#include - -#ifdef HAVE_LEAPMOTION -#include -#endif - -/// Handles interaction with the Leapmotion skeleton tracking suit. -class Leapmotion : public MotionTracker { -public: - static const Name NAME; - - static void init(); - static void destroy(); - - /// Leapmotion MotionTracker factory - static Leapmotion* getInstance(); - - bool isActive() const { return _active; } - - virtual void update() override; - -protected: - Leapmotion(); - virtual ~Leapmotion(); - -private: -#ifdef HAVE_LEAPMOTION - Leap::Listener _listener; - Leap::Controller _controller; -#endif - - bool _active; -}; - -#endif // hifi_Leapmotion_h diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index f3ec3cd79d..9fbd01817a 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -17,7 +17,6 @@ #include #include "Application.h" -#include void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { if (event->type() == HFActionEvent::startType()) { @@ -97,86 +96,6 @@ QVariant ControllerScriptingInterface::getRecommendedOverlayRect() const { return qRectToVariant(rect); } -controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { - // This is where we retrieve the Device Tracker category and then the sub tracker within it - auto icIt = _inputControllers.find(0); - if (icIt != _inputControllers.end()) { - return (*icIt).second.get(); - } - - // 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) { - controller::InputController::Pointer inputController = std::make_shared(deviceID, trackerID, this); - controller::InputController::Key key = inputController->getKey(); - _inputControllers.insert(InputControllerMap::value_type(key, inputController)); - return inputController.get(); - } - } - } - - return nullptr; -} - -void ControllerScriptingInterface::releaseInputController(controller::InputController* input) { - _inputControllers.erase(input->getKey()); -} - -void ControllerScriptingInterface::updateInputControllers() { - for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) { - (*it).second->update(); - } -} - -InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : - _deviceTrackerId(deviceTrackerId), - _subTrackerId(subTrackerId), - _isActive(false) -{ -} - -void InputController::update() { - _isActive = false; - - // TODO for now the InputController is only supporting a JointTracker from a MotionTracker - MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId)); - if (motionTracker) { - if ((int)_subTrackerId < motionTracker->numJointTrackers()) { - const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId); - - if (joint->isActive()) { - joint->getAbsFrame().getTranslation(_eventCache.absTranslation); - joint->getAbsFrame().getRotation(_eventCache.absRotation); - joint->getLocFrame().getTranslation(_eventCache.locTranslation); - joint->getLocFrame().getRotation(_eventCache.locRotation); - - _isActive = true; - //emit spatialEvent(_eventCache); - } - } - } -} - -const unsigned int INPUTCONTROLLER_KEY_DEVICE_OFFSET = 16; -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)); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 996ccabb20..6d197477bb 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -25,38 +25,6 @@ #include class ScriptEngine; -class PalmData; - -class InputController : public controller::InputController { - Q_OBJECT - -public: - InputController(int deviceTrackerId, int subTrackerId, QObject* parent = NULL); - - virtual void update() override; - virtual Key getKey() const override; - -public slots: - - virtual bool isActive() const override { return _isActive; } - virtual glm::vec3 getAbsTranslation() const override { return _eventCache.absTranslation; } - virtual glm::quat getAbsRotation() const override { return _eventCache.absRotation; } - virtual glm::vec3 getLocTranslation() const override { return _eventCache.locTranslation; } - virtual glm::quat getLocRotation() const override { return _eventCache.locRotation; } - -private: - - int _deviceTrackerId; - uint _subTrackerId; - - // cache for the spatial - SpatialEvent _eventCache; - bool _isActive; - -signals: -}; - - /// handles scripting of input controller commands from JS class ControllerScriptingInterface : public controller::ScriptingInterface { Q_OBJECT @@ -86,8 +54,6 @@ public: bool isJoystickCaptured(int joystickIndex) const; bool areEntityClicksCaptured() const; - void updateInputControllers(); - public slots: virtual void captureKeyEvents(const KeyEvent& event); @@ -102,10 +68,6 @@ public slots: virtual glm::vec2 getViewportDimensions() const; virtual QVariant getRecommendedOverlayRect() const; - /// Factory to create an InputController - virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker); - virtual void releaseInputController(controller::InputController* input); - signals: void keyPressEvent(const KeyEvent& event); void keyReleaseEvent(const KeyEvent& event); @@ -135,8 +97,6 @@ private: bool _captureEntityClicks; using InputKey = controller::InputController::Key; - using InputControllerMap = std::map; - InputControllerMap _inputControllers; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 1147b8a811..5023bd7ae5 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1978,7 +1978,7 @@ JointData jointDataFromJsonValue(const QJsonValue& json) { result.rotation = quatFromJsonValue(array[0]); result.rotationSet = true; result.translation = vec3FromJsonValue(array[1]); - result.translationSet = false; + result.translationSet = true; } return result; } @@ -2146,12 +2146,9 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { QVector jointArray; QJsonArray jointArrayJson = json[JSON_AVATAR_JOINT_ARRAY].toArray(); jointArray.reserve(jointArrayJson.size()); - int i = 0; for (const auto& jointJson : jointArrayJson) { auto joint = jointDataFromJsonValue(jointJson); jointArray.push_back(joint); - setJointData(i, joint.rotation, joint.translation); - i++; } setRawJointData(jointArray); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 63bdd1112f..d6241c2c50 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -627,6 +627,7 @@ public: void markIdentityDataChanged() { _identityDataChanged = true; } void pushIdentitySequenceNumber() { ++_identitySequenceNumber; }; + bool hasProcessedFirstIdentity() const { return _hasProcessedFirstIdentity; } float getDensity() const { return _density; } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 96e433bcc8..b3c0ed3f05 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -59,6 +59,48 @@ namespace controller { makePosePair(Action::SPINE2, "Spine2"), makePosePair(Action::HEAD, "Head"), + makePosePair(Action::LEFT_HAND_THUMB1, "LeftHandThumb1"), + makePosePair(Action::LEFT_HAND_THUMB2, "LeftHandThumb2"), + makePosePair(Action::LEFT_HAND_THUMB3, "LeftHandThumb3"), + makePosePair(Action::LEFT_HAND_THUMB4, "LeftHandThumb4"), + makePosePair(Action::LEFT_HAND_INDEX1, "LeftHandIndex1"), + makePosePair(Action::LEFT_HAND_INDEX2, "LeftHandIndex2"), + makePosePair(Action::LEFT_HAND_INDEX3, "LeftHandIndex3"), + makePosePair(Action::LEFT_HAND_INDEX4, "LeftHandIndex4"), + makePosePair(Action::LEFT_HAND_MIDDLE1, "LeftHandMiddle1"), + makePosePair(Action::LEFT_HAND_MIDDLE2, "LeftHandMiddle2"), + makePosePair(Action::LEFT_HAND_MIDDLE3, "LeftHandMiddle3"), + makePosePair(Action::LEFT_HAND_MIDDLE4, "LeftHandMiddle4"), + makePosePair(Action::LEFT_HAND_RING1, "LeftHandRing1"), + makePosePair(Action::LEFT_HAND_RING2, "LeftHandRing2"), + makePosePair(Action::LEFT_HAND_RING3, "LeftHandRing3"), + makePosePair(Action::LEFT_HAND_RING4, "LeftHandRing4"), + makePosePair(Action::LEFT_HAND_PINKY1, "LeftHandPinky1"), + makePosePair(Action::LEFT_HAND_PINKY2, "LeftHandPinky2"), + makePosePair(Action::LEFT_HAND_PINKY3, "LeftHandPinky3"), + makePosePair(Action::LEFT_HAND_PINKY4, "LeftHandPinky4"), + + makePosePair(Action::RIGHT_HAND_THUMB1, "RightHandThumb1"), + makePosePair(Action::RIGHT_HAND_THUMB2, "RightHandThumb2"), + makePosePair(Action::RIGHT_HAND_THUMB3, "RightHandThumb3"), + makePosePair(Action::RIGHT_HAND_THUMB4, "RightHandThumb4"), + makePosePair(Action::RIGHT_HAND_INDEX1, "RightHandIndex1"), + makePosePair(Action::RIGHT_HAND_INDEX2, "RightHandIndex2"), + makePosePair(Action::RIGHT_HAND_INDEX3, "RightHandIndex3"), + makePosePair(Action::RIGHT_HAND_INDEX4, "RightHandIndex4"), + makePosePair(Action::RIGHT_HAND_MIDDLE1, "RightHandMiddle1"), + makePosePair(Action::RIGHT_HAND_MIDDLE2, "RightHandMiddle2"), + makePosePair(Action::RIGHT_HAND_MIDDLE3, "RightHandMiddle3"), + makePosePair(Action::RIGHT_HAND_MIDDLE4, "RightHandMiddle4"), + makePosePair(Action::RIGHT_HAND_RING1, "RightHandRing1"), + makePosePair(Action::RIGHT_HAND_RING2, "RightHandRing2"), + makePosePair(Action::RIGHT_HAND_RING3, "RightHandRing3"), + makePosePair(Action::RIGHT_HAND_RING4, "RightHandRing4"), + makePosePair(Action::RIGHT_HAND_PINKY1, "RightHandPinky1"), + makePosePair(Action::RIGHT_HAND_PINKY2, "RightHandPinky2"), + makePosePair(Action::RIGHT_HAND_PINKY3, "RightHandPinky3"), + makePosePair(Action::RIGHT_HAND_PINKY4, "RightHandPinky4"), + makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index 2cb500c42a..ec4800c9aa 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -104,6 +104,47 @@ enum class Action { LEFT_ARM, RIGHT_ARM, + LEFT_HAND_THUMB1, + LEFT_HAND_THUMB2, + LEFT_HAND_THUMB3, + LEFT_HAND_THUMB4, + LEFT_HAND_INDEX1, + LEFT_HAND_INDEX2, + LEFT_HAND_INDEX3, + LEFT_HAND_INDEX4, + LEFT_HAND_MIDDLE1, + LEFT_HAND_MIDDLE2, + LEFT_HAND_MIDDLE3, + LEFT_HAND_MIDDLE4, + LEFT_HAND_RING1, + LEFT_HAND_RING2, + LEFT_HAND_RING3, + LEFT_HAND_RING4, + LEFT_HAND_PINKY1, + LEFT_HAND_PINKY2, + LEFT_HAND_PINKY3, + LEFT_HAND_PINKY4, + + RIGHT_HAND_THUMB1, + RIGHT_HAND_THUMB2, + RIGHT_HAND_THUMB3, + RIGHT_HAND_THUMB4, + RIGHT_HAND_INDEX1, + RIGHT_HAND_INDEX2, + RIGHT_HAND_INDEX3, + RIGHT_HAND_INDEX4, + RIGHT_HAND_MIDDLE1, + RIGHT_HAND_MIDDLE2, + RIGHT_HAND_MIDDLE3, + RIGHT_HAND_MIDDLE4, + RIGHT_HAND_RING1, + RIGHT_HAND_RING2, + RIGHT_HAND_RING3, + RIGHT_HAND_RING4, + RIGHT_HAND_PINKY1, + RIGHT_HAND_PINKY2, + RIGHT_HAND_PINKY3, + RIGHT_HAND_PINKY4, NUM_ACTIONS, }; diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 8e49bb0ebf..40b87bc6b2 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -101,7 +101,47 @@ Input::NamedVector StandardController::getAvailableInputs() const { // Poses makePair(LEFT_HAND, "LeftHand"), + makePair(LEFT_HAND_THUMB1, "LeftHandThumb1"), + makePair(LEFT_HAND_THUMB2, "LeftHandThumb2"), + makePair(LEFT_HAND_THUMB3, "LeftHandThumb3"), + makePair(LEFT_HAND_THUMB4, "LeftHandThumb4"), + makePair(LEFT_HAND_INDEX1, "LeftHandIndex1"), + makePair(LEFT_HAND_INDEX2, "LeftHandIndex2"), + makePair(LEFT_HAND_INDEX3, "LeftHandIndex3"), + makePair(LEFT_HAND_INDEX4, "LeftHandIndex4"), + makePair(LEFT_HAND_MIDDLE1, "LeftHandMiddle1"), + makePair(LEFT_HAND_MIDDLE2, "LeftHandMiddle2"), + makePair(LEFT_HAND_MIDDLE3, "LeftHandMiddle3"), + makePair(LEFT_HAND_MIDDLE4, "LeftHandMiddle4"), + makePair(LEFT_HAND_RING1, "LeftHandRing1"), + makePair(LEFT_HAND_RING2, "LeftHandRing2"), + makePair(LEFT_HAND_RING3, "LeftHandRing3"), + makePair(LEFT_HAND_RING4, "LeftHandRing4"), + makePair(LEFT_HAND_PINKY1, "LeftHandPinky1"), + makePair(LEFT_HAND_PINKY2, "LeftHandPinky2"), + makePair(LEFT_HAND_PINKY3, "LeftHandPinky3"), + makePair(LEFT_HAND_PINKY4, "LeftHandPinky4"), makePair(RIGHT_HAND, "RightHand"), + makePair(RIGHT_HAND_THUMB1, "RightHandThumb1"), + makePair(RIGHT_HAND_THUMB2, "RightHandThumb2"), + makePair(RIGHT_HAND_THUMB3, "RightHandThumb3"), + makePair(RIGHT_HAND_THUMB4, "RightHandThumb4"), + makePair(RIGHT_HAND_INDEX1, "RightHandIndex1"), + makePair(RIGHT_HAND_INDEX2, "RightHandIndex2"), + makePair(RIGHT_HAND_INDEX3, "RightHandIndex3"), + makePair(RIGHT_HAND_INDEX4, "RightHandIndex4"), + makePair(RIGHT_HAND_MIDDLE1, "RightHandMiddle1"), + makePair(RIGHT_HAND_MIDDLE2, "RightHandMiddle2"), + makePair(RIGHT_HAND_MIDDLE3, "RightHandMiddle3"), + makePair(RIGHT_HAND_MIDDLE4, "RightHandMiddle4"), + makePair(RIGHT_HAND_RING1, "RightHandRing1"), + makePair(RIGHT_HAND_RING2, "RightHandRing2"), + makePair(RIGHT_HAND_RING3, "RightHandRing3"), + makePair(RIGHT_HAND_RING4, "RightHandRing4"), + makePair(RIGHT_HAND_PINKY1, "RightHandPinky1"), + makePair(RIGHT_HAND_PINKY2, "RightHandPinky2"), + makePair(RIGHT_HAND_PINKY3, "RightHandPinky3"), + makePair(RIGHT_HAND_PINKY4, "RightHandPinky4"), makePair(LEFT_FOOT, "LeftFoot"), makePair(RIGHT_FOOT, "RightFoot"), makePair(RIGHT_ARM, "RightArm"), diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 3441407f62..11e67811b6 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -114,7 +114,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_getQuery), (&::gpu::gl::GLBackend::do_resetStages), - + + (&::gpu::gl::GLBackend::do_disableContextViewCorrection), + (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), (&::gpu::gl::GLBackend::do_disableContextStereo), (&::gpu::gl::GLBackend::do_restoreContextStereo), @@ -374,6 +376,13 @@ void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); } +void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._viewCorrectionEnabled = false; +} + +void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._viewCorrectionEnabled = true; +} void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 96217555e1..88aecda617 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -143,6 +143,10 @@ public: // Reset stages virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; + + virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final; + virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; @@ -333,6 +337,8 @@ protected: bool _skybox { false }; Transform _view; CameraCorrection _correction; + bool _viewCorrectionEnabled{ true }; + Mat4 _projection; Vec4i _viewport { 0, 0, 1, 1 }; @@ -400,6 +406,7 @@ protected: bool _invalidProgram { false }; BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; State::Data _stateCache{ State::DEFAULT }; State::Signature _stateSignatureCache { 0 }; @@ -409,6 +416,8 @@ protected: PipelineStageState() { _cameraCorrectionBuffer.edit() = CameraCorrection(); + _cameraCorrectionBufferIdentity.edit() = CameraCorrection(); + _cameraCorrectionBufferIdentity._buffer->flush(); } } _pipeline; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 2d71e8ed78..7ef64343ea 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -77,8 +77,14 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { if (_pipeline._invalidProgram) { glUseProgram(_pipeline._program); if (_pipeline._cameraCorrectionLocation != -1) { - auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + gl::GLBuffer* cameraCorrectionBuffer = nullptr; + if (_transform._viewCorrectionEnabled) { + cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + } else { + cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer); + } glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); + } (void) CHECK_GL_ERROR(); _pipeline._invalidProgram = false; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp index 01f055e0d9..f286a5cca9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp @@ -102,7 +102,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView) { // Apply the correction - if (_viewIsCamera && _correction.correction != glm::mat4()) { + if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; _view.mult(result, _view, _correction.correction); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 15c0dfce49..c432e19368 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -390,6 +390,13 @@ void Batch::resetStages() { ADD_COMMAND(resetStages); } +void Batch::disableContextViewCorrection() { + ADD_COMMAND(disableContextViewCorrection); +} + +void Batch::restoreContextViewCorrection() { + ADD_COMMAND(restoreContextViewCorrection); +} void Batch::disableContextStereo() { ADD_COMMAND(disableContextStereo); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 27c9402131..77d22258b2 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -217,6 +217,9 @@ public: // Reset the stage caches and states void resetStages(); + void disableContextViewCorrection(); + void restoreContextViewCorrection(); + void disableContextStereo(); void restoreContextStereo(); @@ -304,6 +307,9 @@ public: COMMAND_resetStages, + COMMAND_disableContextViewCorrection, + COMMAND_restoreContextViewCorrection, + COMMAND_disableContextStereo, COMMAND_restoreContextStereo, diff --git a/libraries/render-utils/src/DeferredFrameTransform.cpp b/libraries/render-utils/src/DeferredFrameTransform.cpp index b1166437db..baf523312c 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.cpp +++ b/libraries/render-utils/src/DeferredFrameTransform.cpp @@ -39,7 +39,7 @@ void DeferredFrameTransform::update(RenderArgs* args) { args->getViewFrustum().evalProjectionMatrix(frameTransformBuffer.projectionMono); // Running in stero ? - bool isStereo = args->_context->isStereo(); + bool isStereo = args->isStereo(); if (!isStereo) { frameTransformBuffer.projection[0] = frameTransformBuffer.projectionMono; frameTransformBuffer.stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index b49a066bf7..20c999019b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -406,7 +406,7 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer batch.setFramebuffer(blitFbo); if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { - if (renderArgs->_context->isStereo()) { + if (renderArgs->isStereo()) { gpu::Vec4i srcRectLeft; srcRectLeft.z = width / 2; srcRectLeft.w = height; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index 1941766353..c4eea7ce7f 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -459,7 +459,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext, auto diffuseVPipeline = _diffusePass.getBlurVPipeline(); auto diffuseHPipeline = _diffusePass.getBlurHPipeline(); - _diffusePass.getParameters()->setWidthHeight(curvatureViewport.z, curvatureViewport.w, args->_context->isStereo()); + _diffusePass.getParameters()->setWidthHeight(curvatureViewport.z, curvatureViewport.w, args->isStereo()); glm::ivec2 textureSize(curvatureTexture->getDimensions()); _diffusePass.getParameters()->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, curvatureViewport)); _diffusePass.getParameters()->setDepthPerspective(args->getViewFrustum().getProjection()[1][1]); diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index a75488ce7a..c2e03d4f46 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -99,6 +99,8 @@ namespace render { void pushViewFrustum(const ViewFrustum& viewFrustum) { _viewFrustums.push(viewFrustum); } void popViewFrustum() { _viewFrustums.pop(); } + bool isStereo() const { return _displayMode != MONO; } + std::shared_ptr _context; std::shared_ptr _blitFramebuffer; std::shared_ptr _pipeline; diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 0a6b3d16fc..73a8e0a0dd 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -217,7 +217,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra auto blurVPipeline = getBlurVPipeline(); auto blurHPipeline = getBlurHPipeline(); - _parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->_context->isStereo()); + _parameters->setWidthHeight(args->_viewport.z, args->_viewport.w, args->isStereo()); glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions()); _parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, args->_viewport)); @@ -330,7 +330,7 @@ void BlurGaussianDepthAware::run(const RenderContextPointer& renderContext, cons auto sourceViewport = args->_viewport; - _parameters->setWidthHeight(sourceViewport.z, sourceViewport.w, args->_context->isStereo()); + _parameters->setWidthHeight(sourceViewport.z, sourceViewport.w, args->isStereo()); glm::ivec2 textureSize(blurringResources.sourceTexture->getDimensions()); _parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, sourceViewport)); _parameters->setDepthPerspective(args->getViewFrustum().getProjection()[1][1]); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index a68d27e620..58b8aead45 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -1076,3 +1076,24 @@ void setMaxCores(uint8_t maxCores) { SetProcessAffinityMask(process, newProcessAffinity); #endif } + +#ifdef Q_OS_WIN +VOID CALLBACK parentDiedCallback(PVOID lpParameter, BOOLEAN timerOrWaitFired) { + if (!timerOrWaitFired && qApp) { + qDebug() << "Parent process died, quitting"; + qApp->quit(); + } +} + +void watchParentProcess(int parentPID) { + DWORD processID = parentPID; + HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); + + HANDLE newHandle; + RegisterWaitForSingleObject(&newHandle, procHandle, parentDiedCallback, NULL, INFINITE, WT_EXECUTEONLYONCE); +} +#else +void watchParentProcess(int parentPID) { + qWarning() << "Parent PID option not implemented on this plateform"; +} +#endif // Q_OS_WIN \ No newline at end of file diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 0f30842f47..681418a263 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -235,4 +235,7 @@ const QString& getInterfaceSharedMemoryName(); void setMaxCores(uint8_t maxCores); +const QString PARENT_PID_OPTION = "parent-pid"; +void watchParentProcess(int parentPID); + #endif // hifi_SharedUtil_h diff --git a/libraries/trackers/src/trackers/DeviceTracker.cpp b/libraries/trackers/src/trackers/DeviceTracker.cpp deleted file mode 100644 index 93aeb607bc..0000000000 --- a/libraries/trackers/src/trackers/DeviceTracker.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// -// Created by Sam Cake on 6/20/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 "DeviceTracker.h" - -DeviceTracker::SingletonData::~SingletonData() { - // Destroy all the device registered - //TODO C++11 for (auto device = _devicesVector.begin(); device != _devicesVector.end(); device++) { - for (Vector::iterator device = _devicesVector.begin(); device != _devicesVector.end(); device++) { - delete (*device); - } -} - -int DeviceTracker::getNumDevices() { - return (int)Singleton::get()->_devicesMap.size(); -} - -DeviceTracker::ID DeviceTracker::getDeviceID(const Name& name) { - //TODO C++11 auto deviceIt = Singleton::get()->_devicesMap.find(name); - Map::iterator deviceIt = Singleton::get()->_devicesMap.find(name); - if (deviceIt != Singleton::get()->_devicesMap.end()) { - return (*deviceIt).second; - } else { - return INVALID_DEVICE; - } -} - -DeviceTracker* DeviceTracker::getDevice(const Name& name) { - return getDevice(getDeviceID(name)); -} - -DeviceTracker* DeviceTracker::getDevice(DeviceTracker::ID deviceID) { - if ((deviceID >= 0) && (deviceID < (int)(Singleton::get()->_devicesVector.size()))) { - return Singleton::get()->_devicesVector[ deviceID ]; - } else { - return NULL; - } -} - -DeviceTracker::ID DeviceTracker::registerDevice(const Name& name, DeviceTracker* device) { - // Check that the device exists, if not exit - if (!device) { - return INVALID_DEVICE; - } - - // Look if the name is not already used - ID deviceID = getDeviceID(name); - if (deviceID >= 0) { - return INVALID_DEVICE_NAME; - } - - // Good to register the device - deviceID = (ID)Singleton::get()->_devicesVector.size(); - Singleton::get()->_devicesMap.insert(Map::value_type(name, deviceID)); - Singleton::get()->_devicesVector.push_back(device); - device->assignIDAndName(deviceID, name); - - return deviceID; -} - -void DeviceTracker::destroyDevice(const Name& name) { - DeviceTracker::ID deviceID = getDeviceID(name); - if (deviceID != INVALID_DEVICE) { - delete Singleton::get()->_devicesVector[getDeviceID(name)]; - Singleton::get()->_devicesVector[getDeviceID(name)] = nullptr; - } -} - -void DeviceTracker::updateAll() { - //TODO C++11 for (auto deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) { - for (Vector::iterator deviceIt = Singleton::get()->_devicesVector.begin(); deviceIt != Singleton::get()->_devicesVector.end(); deviceIt++) { - if ((*deviceIt)) - (*deviceIt)->update(); - } -} - -// Core features of the Device Tracker -DeviceTracker::DeviceTracker() : - _ID(INVALID_DEVICE), - _name("Unkown") -{ -} - -DeviceTracker::~DeviceTracker() { -} - -void DeviceTracker::update() { -} diff --git a/libraries/trackers/src/trackers/DeviceTracker.h b/libraries/trackers/src/trackers/DeviceTracker.h deleted file mode 100644 index 8a7f509cb3..0000000000 --- a/libraries/trackers/src/trackers/DeviceTracker.h +++ /dev/null @@ -1,115 +0,0 @@ -// -// Created by Sam Cake on 6/20/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 -// - -#ifndef hifi_DeviceTracker_h -#define hifi_DeviceTracker_h - -#include -#include -#include - -// Singleton template class -template < typename T > -class TemplateSingleton { -public: - - static T* get() { - if ( !_singleton._one ) { - _singleton._one = new T(); - } - return _singleton._one; - } - - TemplateSingleton() : - _one(0) - { - } - ~TemplateSingleton() { - if ( _one ) { - delete _one; - _one = 0; - } - } -private: - static TemplateSingleton< T > _singleton; - T* _one; -}; -template -TemplateSingleton TemplateSingleton::_singleton; - -/// Base class for device trackers. -class DeviceTracker { -public: - - // THe ID and Name types used to manage the pool of devices - typedef std::string Name; - typedef int ID; - static const ID INVALID_DEVICE = -1; - static const ID INVALID_DEVICE_NAME = -2; - - // Singleton interface to register and query the devices currently connected - static int getNumDevices(); - static ID getDeviceID(const Name& name); - static DeviceTracker* getDevice(ID deviceID); - static DeviceTracker* getDevice(const Name& name); - - /// Update all the devices calling for their update() function - /// This should be called every frame by the main loop to update all the devices that pull their state - static void updateAll(); - - /// Register a device tracker to the factory - /// Right after creating a new DeviceTracker, it should be registered - /// This is why, it's recommended to use a factory static call in the specialized class - /// to create a new input device - /// - /// \param name The Name under wich registering the device - /// \param parent The DeviceTracker - /// - /// \return The Index of the newly registered device. - /// Valid if everything went well. - /// INVALID_DEVICE if the device is not valid (NULL) - /// INVALID_DEVICE_NAME if the name is already taken - static ID registerDevice(const Name& name, DeviceTracker* tracker); - - static void destroyDevice(const Name& name); - - // DeviceTracker interface - - virtual void update(); - - /// Get the ID assigned to the Device when registered after creation, or INVALID_DEVICE if it hasn't been registered which should not happen. - ID getID() const { return _ID; } - - /// Get the name assigned to the Device when registered after creation, or "Unknown" if it hasn't been registered which should not happen. - const Name& getName() const { return _name; } - - typedef std::map< Name, ID > Map; - static const Map& getDevices() { return Singleton::get()->_devicesMap; } - -protected: - DeviceTracker(); - virtual ~DeviceTracker(); - -private: - ID _ID; - Name _name; - - // this call is used by the singleton when the device tracker is currently beeing registered and beeing assigned an ID - void assignIDAndName( const ID id, const Name& name ) { _ID = id; _name = name; } - - typedef std::vector< DeviceTracker* > Vector; - struct SingletonData { - Map _devicesMap; - Vector _devicesVector; - - ~SingletonData(); - }; - typedef TemplateSingleton< SingletonData > Singleton; -}; - -#endif // hifi_DeviceTracker_h diff --git a/libraries/trackers/src/trackers/MotionTracker.cpp b/libraries/trackers/src/trackers/MotionTracker.cpp deleted file mode 100644 index c6012c0422..0000000000 --- a/libraries/trackers/src/trackers/MotionTracker.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// -// Created by Sam Cake on 6/20/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 "MotionTracker.h" - -// glm::mult(mat43, mat43) just the composition of the 2 matrices assuming they are in fact mat44 with the last raw = { 0, 0, 0, 1 } -namespace glm { - mat4x3 mult(const mat4& lhs, const mat4x3& rhs) { - vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x); - vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y); - vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z); - return mat4x3( - dot(lrx, rhs[0]), - dot(lry, rhs[0]), - dot(lrz, rhs[0]), - - dot(lrx, rhs[1]), - dot(lry, rhs[1]), - dot(lrz, rhs[1]), - - dot(lrx, rhs[2]), - dot(lry, rhs[2]), - dot(lrz, rhs[2]), - - dot(lrx, rhs[3]) + lhs[3].x, - dot(lry, rhs[3]) + lhs[3].y, - dot(lrz, rhs[3]) + lhs[3].z - ); - } - mat4x3 mult(const mat4x3& lhs, const mat4x3& rhs) { - vec3 lrx(lhs[0].x, lhs[1].x, lhs[2].x); - vec3 lry(lhs[0].y, lhs[1].y, lhs[2].y); - vec3 lrz(lhs[0].z, lhs[1].z, lhs[2].z); - return mat4x3( - dot(lrx, rhs[0]), - dot(lry, rhs[0]), - dot(lrz, rhs[0]), - - dot(lrx, rhs[1]), - dot(lry, rhs[1]), - dot(lrz, rhs[1]), - - dot(lrx, rhs[2]), - dot(lry, rhs[2]), - dot(lrz, rhs[2]), - - dot(lrx, rhs[3]) + lhs[3].x, - dot(lry, rhs[3]) + lhs[3].y, - dot(lrz, rhs[3]) + lhs[3].z - ); - } -} - -// MotionTracker -MotionTracker::MotionTracker() : - DeviceTracker() -{ - _jointsArray.resize(1); - _jointsMap.insert(JointTracker::Map::value_type(Semantic("Root"), 0)); -} - -MotionTracker::~MotionTracker() -{ -} - -bool MotionTracker::isConnected() const { - return false; -} - -MotionTracker::Index MotionTracker::addJoint(const Semantic& semantic, Index parent) { - // Check the parent - if (int(parent) < 0) - return INVALID_PARENT; - - // Check that the semantic is not already in use - Index foundIndex = findJointIndex(semantic); - if (foundIndex >= 0) { - return INVALID_SEMANTIC; - } - - - // All good then allocate the joint - Index newIndex = (Index)_jointsArray.size(); - _jointsArray.push_back(JointTracker(semantic, parent)); - _jointsMap.insert(JointTracker::Map::value_type(semantic, newIndex)); - - return newIndex; -} - -MotionTracker::Index MotionTracker::findJointIndex(const Semantic& semantic) const { - // TODO C++11 auto jointIt = _jointsMap.find(semantic); - JointTracker::Map::const_iterator jointIt = _jointsMap.find(semantic); - if (jointIt != _jointsMap.end()) { - return (*jointIt).second; - } - - return INVALID_SEMANTIC; -} - -void MotionTracker::updateAllAbsTransform() { - _jointsArray[0].updateAbsFromLocTransform(0); - - // Because we know the hierarchy is stored from root down the branches let's just traverse and update - for (Index i = 1; i < (Index)(_jointsArray.size()); i++) { - JointTracker* joint = _jointsArray.data() + i; - joint->updateAbsFromLocTransform(_jointsArray.data() + joint->getParent()); - } -} - - -// MotionTracker::JointTracker -MotionTracker::JointTracker::JointTracker() : - _locFrame(), - _absFrame(), - _semantic(""), - _parent(INVALID_PARENT), - _lastUpdate(1) // Joint inactive -{ -} - -MotionTracker::JointTracker::JointTracker(const Semantic& semantic, Index parent) : - _semantic(semantic), - _parent(parent), - _lastUpdate(1) // Joint inactive -{ -} - -MotionTracker::JointTracker::JointTracker(const JointTracker& tracker) : - _locFrame(tracker._locFrame), - _absFrame(tracker._absFrame), - _semantic(tracker._semantic), - _parent(tracker._parent), - _lastUpdate(tracker._lastUpdate) -{ -} - -void MotionTracker::JointTracker::updateAbsFromLocTransform(const JointTracker* parentJoint) { - if (parentJoint) { - editAbsFrame()._transform = (parentJoint->getAbsFrame()._transform * getLocFrame()._transform); - } else { - editAbsFrame()._transform = getLocFrame()._transform; - } -} - -void MotionTracker::JointTracker::updateLocFromAbsTransform(const JointTracker* parentJoint) { - if (parentJoint) { - glm::mat4 ip = glm::inverse(parentJoint->getAbsFrame()._transform); - editLocFrame()._transform = (ip * getAbsFrame()._transform); - } else { - editLocFrame()._transform = getAbsFrame()._transform; - } -} - -//-------------------------------------------------------------------------------------- -// MotionTracker::Frame -//-------------------------------------------------------------------------------------- - -MotionTracker::Frame::Frame() : - _transform() -{ -} - -void MotionTracker::Frame::setRotation(const glm::quat& rotation) { - glm::mat3x3 rot = glm::mat3_cast(rotation); - _transform[0] = glm::vec4(rot[0], 0.0f); - _transform[1] = glm::vec4(rot[1], 0.0f); - _transform[2] = glm::vec4(rot[2], 0.0f); -} - -void MotionTracker::Frame::getRotation(glm::quat& rotation) const { - rotation = glm::quat_cast(_transform); -} - -void MotionTracker::Frame::setTranslation(const glm::vec3& translation) { - _transform[3] = glm::vec4(translation, 1.0f); -} - -void MotionTracker::Frame::getTranslation(glm::vec3& translation) const { - translation = extractTranslation(_transform); -} - diff --git a/libraries/trackers/src/trackers/MotionTracker.h b/libraries/trackers/src/trackers/MotionTracker.h deleted file mode 100644 index 26c8dcea2c..0000000000 --- a/libraries/trackers/src/trackers/MotionTracker.h +++ /dev/null @@ -1,116 +0,0 @@ -// -// Created by Sam Cake on 6/20/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 -// - -#ifndef hifi_MotionTracker_h -#define hifi_MotionTracker_h - -#include "DeviceTracker.h" - -#include - -/// Base class for device trackers. -class MotionTracker : public DeviceTracker { -public: - - class Frame { - public: - Frame(); - - glm::mat4 _transform; - - void setRotation(const glm::quat& rotation); - void getRotation(glm::quat& rotatio) const; - - void setTranslation(const glm::vec3& translation); - void getTranslation(glm::vec3& translation) const; - }; - - // Semantic and Index types to retreive the JointTrackers of this MotionTracker - typedef std::string Semantic; - typedef int32_t Index; - static const Index INVALID_SEMANTIC = -1; - static const Index INVALID_PARENT = -2; - - class JointTracker { - public: - typedef std::vector< JointTracker > Vector; - typedef std::map< Semantic, Index > Map; - - JointTracker(); - JointTracker(const JointTracker& tracker); - JointTracker(const Semantic& semantic, Index parent); - - const Frame& getLocFrame() const { return _locFrame; } - Frame& editLocFrame() { return _locFrame; } - void setLocFrame(const Frame& frame) { editLocFrame() = frame; } - - const Frame& getAbsFrame() const { return _absFrame; } - Frame& editAbsFrame() { return _absFrame; } - void setAbsFrame(const Frame& frame) { editAbsFrame() = frame; } - - const Semantic& getSemantic() const { return _semantic; } - const Index& getParent() const { return _parent; } - - bool isActive() const { return (_lastUpdate <= 0); } - void tickNewFrame() { _lastUpdate++; } - void activeFrame() { _lastUpdate = 0; } - - /// Update the loc/abs transform for this joint from the current abs/loc value and the specified parent joint abs frame - void updateLocFromAbsTransform(const JointTracker* parentJoint); - void updateAbsFromLocTransform(const JointTracker* parentJoint); - - protected: - Frame _locFrame; - Frame _absFrame; - Semantic _semantic; - Index _parent; - int _lastUpdate; - }; - - virtual bool isConnected() const; - - Index numJointTrackers() const { return (Index)_jointsArray.size(); } - - /// Access a Joint from it's index. - /// Index 0 is always the "Root". - /// if the index is Invalid then returns NULL. - const JointTracker* getJointTracker(Index index) const { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); } - JointTracker* editJointTracker(Index index) { return ((index > 0) && (index < (Index)(_jointsArray.size())) ? _jointsArray.data() + index : NULL); } - - /// From a semantic, find the Index of the Joint. - /// \return the index of the mapped Joint or INVALID_SEMANTIC if the semantic is not knowned. - Index findJointIndex(const Semantic& semantic) const; - -protected: - MotionTracker(); - virtual ~MotionTracker(); - - JointTracker::Vector _jointsArray; - JointTracker::Map _jointsMap; - - /// Adding joint is only done from the specialized Motion Tracker, hence this function is protected. - /// The hierarchy of joints must be created from the top down to the branches. - /// The "Root" node is at index 0 and exists at creation of the Motion Tracker. - /// - /// \param semantic A joint is defined by it's semantic, the unique name mapping to it - /// \param parent The parent's index, the parent must be valid and correspond to a Joint that has been previously created - /// - /// \return The Index of the newly created Joint. - /// Valid if everything went well. - /// INVALID_SEMANTIC if the semantic is already in use - /// INVALID_PARENT if the parent is not valid - Index addJoint(const Semantic& semantic, Index parent); - - /// Update the absolute transform stack traversing the hierarchy from the root down the branches - /// This is a generic way to update all the Joint's absFrame by combining the locFrame going down the hierarchy branch. - void updateAllAbsTransform(); -}; - - - -#endif // hifi_MotionTracker_h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 82ca2a7d38..06cf929368 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -30,6 +30,8 @@ if (NOT SERVER_ONLY AND NOT ANDROID) add_subdirectory(${DIR}) set(DIR "steamClient") add_subdirectory(${DIR}) + set(DIR "hifiLeapMotion") + add_subdirectory(${DIR}) endif() # server-side plugins diff --git a/plugins/hifiLeapMotion/CMakeLists.txt b/plugins/hifiLeapMotion/CMakeLists.txt new file mode 100644 index 0000000000..14f9bbaa17 --- /dev/null +++ b/plugins/hifiLeapMotion/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# Created by David Rowe on 15 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 +# + +find_package(LEAPMOTION) +if (LEAPMOTION_FOUND) + set(TARGET_NAME hifiLeapMotion) + setup_hifi_plugin(Script Qml Widgets) + link_hifi_libraries(shared controllers ui plugins input-plugins) + target_leapmotion() +endif() diff --git a/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp b/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp new file mode 100644 index 0000000000..174dd02426 --- /dev/null +++ b/plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp @@ -0,0 +1,493 @@ +// +// LeapMotionPlugin.cpp +// +// Created by David Rowe on 15 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 +// + +#include "LeapMotionPlugin.h" + +#include + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(inputplugins) +Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") + +const char* LeapMotionPlugin::NAME = "Leap Motion"; +const char* LeapMotionPlugin::LEAPMOTION_ID_STRING = "Leap Motion"; + +const bool DEFAULT_ENABLED = false; +const char* SENSOR_ON_DESKTOP = "Desktop"; +const char* SENSOR_ON_HMD = "HMD"; +const char* DEFAULT_SENSOR_LOCATION = SENSOR_ON_DESKTOP; + +enum LeapMotionJointIndex { + LeftHand = 0, + LeftHandThumb1, + LeftHandThumb2, + LeftHandThumb3, + LeftHandThumb4, + LeftHandIndex1, + LeftHandIndex2, + LeftHandIndex3, + LeftHandIndex4, + LeftHandMiddle1, + LeftHandMiddle2, + LeftHandMiddle3, + LeftHandMiddle4, + LeftHandRing1, + LeftHandRing2, + LeftHandRing3, + LeftHandRing4, + LeftHandPinky1, + LeftHandPinky2, + LeftHandPinky3, + LeftHandPinky4, + RightHand, + RightHandThumb1, + RightHandThumb2, + RightHandThumb3, + RightHandThumb4, + RightHandIndex1, + RightHandIndex2, + RightHandIndex3, + RightHandIndex4, + RightHandMiddle1, + RightHandMiddle2, + RightHandMiddle3, + RightHandMiddle4, + RightHandRing1, + RightHandRing2, + RightHandRing3, + RightHandRing4, + RightHandPinky1, + RightHandPinky2, + RightHandPinky3, + RightHandPinky4, + + Size +}; + +static controller::StandardPoseChannel LeapMotionJointIndexToPoseIndexMap[LeapMotionJointIndex::Size] = { + controller::LEFT_HAND, + controller::LEFT_HAND_THUMB1, + controller::LEFT_HAND_THUMB2, + controller::LEFT_HAND_THUMB3, + controller::LEFT_HAND_THUMB4, + controller::LEFT_HAND_INDEX1, + controller::LEFT_HAND_INDEX2, + controller::LEFT_HAND_INDEX3, + controller::LEFT_HAND_INDEX4, + controller::LEFT_HAND_MIDDLE1, + controller::LEFT_HAND_MIDDLE2, + controller::LEFT_HAND_MIDDLE3, + controller::LEFT_HAND_MIDDLE4, + controller::LEFT_HAND_RING1, + controller::LEFT_HAND_RING2, + controller::LEFT_HAND_RING3, + controller::LEFT_HAND_RING4, + controller::LEFT_HAND_PINKY1, + controller::LEFT_HAND_PINKY2, + controller::LEFT_HAND_PINKY3, + controller::LEFT_HAND_PINKY4, + controller::RIGHT_HAND, + controller::RIGHT_HAND_THUMB1, + controller::RIGHT_HAND_THUMB2, + controller::RIGHT_HAND_THUMB3, + controller::RIGHT_HAND_THUMB4, + controller::RIGHT_HAND_INDEX1, + controller::RIGHT_HAND_INDEX2, + controller::RIGHT_HAND_INDEX3, + controller::RIGHT_HAND_INDEX4, + controller::RIGHT_HAND_MIDDLE1, + controller::RIGHT_HAND_MIDDLE2, + controller::RIGHT_HAND_MIDDLE3, + controller::RIGHT_HAND_MIDDLE4, + controller::RIGHT_HAND_RING1, + controller::RIGHT_HAND_RING2, + controller::RIGHT_HAND_RING3, + controller::RIGHT_HAND_RING4, + controller::RIGHT_HAND_PINKY1, + controller::RIGHT_HAND_PINKY2, + controller::RIGHT_HAND_PINKY3, + controller::RIGHT_HAND_PINKY4 +}; + +#define UNKNOWN_JOINT (controller::StandardPoseChannel)0 + +static controller::StandardPoseChannel LeapMotionJointIndexToPoseIndex(LeapMotionJointIndex i) { + assert(i >= 0 && i < LeapMotionJointIndex::Size); + if (i >= 0 && i < LeapMotionJointIndex::Size) { + return LeapMotionJointIndexToPoseIndexMap[i]; + } else { + return UNKNOWN_JOINT; + } +} + +QStringList controllerJointNames = { + "Hips", + "RightUpLeg", + "RightLeg", + "RightFoot", + "LeftUpLeg", + "LeftLeg", + "LeftFoot", + "Spine", + "Spine1", + "Spine2", + "Spine3", + "Neck", + "Head", + "RightShoulder", + "RightArm", + "RightForeArm", + "RightHand", + "RightHandThumb1", + "RightHandThumb2", + "RightHandThumb3", + "RightHandThumb4", + "RightHandIndex1", + "RightHandIndex2", + "RightHandIndex3", + "RightHandIndex4", + "RightHandMiddle1", + "RightHandMiddle2", + "RightHandMiddle3", + "RightHandMiddle4", + "RightHandRing1", + "RightHandRing2", + "RightHandRing3", + "RightHandRing4", + "RightHandPinky1", + "RightHandPinky2", + "RightHandPinky3", + "RightHandPinky4", + "LeftShoulder", + "LeftArm", + "LeftForeArm", + "LeftHand", + "LeftHandThumb1", + "LeftHandThumb2", + "LeftHandThumb3", + "LeftHandThumb4", + "LeftHandIndex1", + "LeftHandIndex2", + "LeftHandIndex3", + "LeftHandIndex4", + "LeftHandMiddle1", + "LeftHandMiddle2", + "LeftHandMiddle3", + "LeftHandMiddle4", + "LeftHandRing1", + "LeftHandRing2", + "LeftHandRing3", + "LeftHandRing4", + "LeftHandPinky1", + "LeftHandPinky2", + "LeftHandPinky3", + "LeftHandPinky4" +}; + +static const char* getControllerJointName(controller::StandardPoseChannel i) { + if (i >= 0 && i < controller::NUM_STANDARD_POSES) { + return qPrintable(controllerJointNames[i]); + } + return "unknown"; +} + + +void LeapMotionPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + if (!_enabled) { + return; + } + + const auto frame = _controller.frame(); + const auto frameID = frame.id(); + if (_lastFrameID >= frameID) { + // Leap Motion not connected or duplicate frame. + return; + } + + if (!_hasLeapMotionBeenConnected) { + emit deviceConnected(getName()); + _hasLeapMotionBeenConnected = true; + } + + processFrame(frame); // Updates _joints. + + auto joints = _joints; + + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->update(deltaTime, inputCalibrationData, joints, _prevJoints); + }); + + _prevJoints = joints; + + _lastFrameID = frameID; +} + +controller::Input::NamedVector LeapMotionPlugin::InputDevice::getAvailableInputs() const { + static controller::Input::NamedVector availableInputs; + if (availableInputs.size() == 0) { + for (int i = 0; i < LeapMotionJointIndex::Size; i++) { + auto channel = LeapMotionJointIndexToPoseIndex(static_cast(i)); + availableInputs.push_back(makePair(channel, getControllerJointName(channel))); + } + }; + return availableInputs; +} + +QString LeapMotionPlugin::InputDevice::getDefaultMappingConfig() const { + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/leapmotion.json"; + return MAPPING_JSON; +} + +void LeapMotionPlugin::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, + const std::vector& joints, + const std::vector& prevJoints) { + + glm::mat4 controllerToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat; + glm::quat controllerToAvatarRotation = glmExtractRotation(controllerToAvatar); + + glm::vec3 hmdSensorPosition; // HMD + glm::quat hmdSensorOrientation; // HMD + glm::vec3 leapMotionOffset; // Desktop + if (_isLeapOnHMD) { + hmdSensorPosition = extractTranslation(inputCalibrationData.hmdSensorMat); + hmdSensorOrientation = extractRotation(inputCalibrationData.hmdSensorMat); + } else { + // Desktop "zero" position is some distance above the Leap Motion sensor and half the avatar's shoulder-to-hand length + // in front of avatar. + float halfShouldToHandLength = fabsf(extractTranslation(inputCalibrationData.defaultLeftHand).x + - extractTranslation(inputCalibrationData.defaultLeftArm).x) / 2.0f; + leapMotionOffset = glm::vec3(0.0f, _desktopHeightOffset, halfShouldToHandLength); + } + + for (size_t i = 0; i < joints.size(); i++) { + int poseIndex = LeapMotionJointIndexToPoseIndex((LeapMotionJointIndex)i); + + if (joints[i].position == Vectors::ZERO) { + _poseStateMap[poseIndex] = controller::Pose(); + continue; + } + + glm::vec3 pos; + glm::quat rot; + if (_isLeapOnHMD) { + auto jointPosition = joints[i].position; + const glm::vec3 HMD_EYE_TO_LEAP_OFFSET = glm::vec3(0.0f, 0.0f, -0.09f); // Eyes to surface of Leap Motion. + jointPosition = glm::vec3(-jointPosition.x, -jointPosition.z, -jointPosition.y) + HMD_EYE_TO_LEAP_OFFSET; + jointPosition = hmdSensorPosition + hmdSensorOrientation * jointPosition; + pos = transformPoint(controllerToAvatar, jointPosition); + + glm::quat jointOrientation = joints[i].orientation; + jointOrientation = glm::quat(jointOrientation.w, -jointOrientation.x, -jointOrientation.z, -jointOrientation.y); + rot = controllerToAvatarRotation * hmdSensorOrientation * jointOrientation; + } else { + pos = controllerToAvatarRotation * (joints[i].position - leapMotionOffset); + const glm::quat ZERO_HAND_ORIENTATION = glm::quat(glm::vec3(PI_OVER_TWO, PI, 0.0f)); + rot = controllerToAvatarRotation * joints[i].orientation * ZERO_HAND_ORIENTATION; + } + + glm::vec3 linearVelocity, angularVelocity; + if (i < prevJoints.size()) { + linearVelocity = (pos - (prevJoints[i].position * METERS_PER_CENTIMETER)) / deltaTime; // m/s + // quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation. + glm::quat d = glm::log(rot * glm::inverse(prevJoints[i].orientation)); + angularVelocity = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s + } + + _poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVelocity, angularVelocity); + } +} + +void LeapMotionPlugin::init() { + loadSettings(); + + auto preferences = DependencyManager::get(); + static const QString LEAPMOTION_PLUGIN { "Leap Motion" }; + { + auto getter = [this]()->bool { return _enabled; }; + auto setter = [this](bool value) { + _enabled = value; + saveSettings(); + if (!_enabled) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->withLock([&, this]() { + _inputDevice->clearState(); + }); + } + }; + auto preference = new CheckPreference(LEAPMOTION_PLUGIN, "Enabled", getter, setter); + preferences->addPreference(preference); + } + { + auto getter = [this]()->QString { return _sensorLocation; }; + auto setter = [this](QString value) { + _sensorLocation = value; + saveSettings(); + applySensorLocation(); + }; + auto preference = new ComboBoxPreference(LEAPMOTION_PLUGIN, "Sensor location", getter, setter); + QStringList list = { SENSOR_ON_DESKTOP, SENSOR_ON_HMD }; + preference->setItems(list); + preferences->addPreference(preference); + } + { + auto getter = [this]()->float { return _desktopHeightOffset; }; + auto setter = [this](float value) { + _desktopHeightOffset = value; + saveSettings(); + applyDesktopHeightOffset(); + }; + auto preference = new SpinnerPreference(LEAPMOTION_PLUGIN, "Desktop height for horizontal forearms", getter, setter); + float MIN_VALUE = 0.0f; + float MAX_VALUE = 1.0f; + float DECIMALS = 2.0f; + float STEP = 0.01f; + preference->setMin(MIN_VALUE); + preference->setMax(MAX_VALUE); + preference->setDecimals(DECIMALS); + preference->setStep(STEP); + preferences->addPreference(preference); + } +} + +bool LeapMotionPlugin::activate() { + InputPlugin::activate(); + + // Nothing required to be done to start up Leap Motion library. + + _joints.resize(LeapMotionJointIndex::Size, { glm::vec3(), glm::quat() }); + + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(_inputDevice); + + return true; +} + +void LeapMotionPlugin::deactivate() { + if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(_inputDevice->_deviceID); + } + + InputPlugin::deactivate(); +} + +const char* SETTINGS_ENABLED_KEY = "enabled"; +const char* SETTINGS_SENSOR_LOCATION_KEY = "sensorLocation"; +const char* SETTINGS_DESKTOP_HEIGHT_OFFSET_KEY = "desktopHeightOffset"; + +void LeapMotionPlugin::saveSettings() const { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + settings.setValue(QString(SETTINGS_ENABLED_KEY), _enabled); + settings.setValue(QString(SETTINGS_SENSOR_LOCATION_KEY), _sensorLocation); + settings.setValue(QString(SETTINGS_DESKTOP_HEIGHT_OFFSET_KEY), _desktopHeightOffset); + } + settings.endGroup(); +} + +void LeapMotionPlugin::loadSettings() { + Settings settings; + QString idString = getID(); + settings.beginGroup(idString); + { + _enabled = settings.value(SETTINGS_ENABLED_KEY, QVariant(DEFAULT_ENABLED)).toBool(); + _sensorLocation = settings.value(SETTINGS_SENSOR_LOCATION_KEY, QVariant(DEFAULT_SENSOR_LOCATION)).toString(); + _desktopHeightOffset = + settings.value(SETTINGS_DESKTOP_HEIGHT_OFFSET_KEY, QVariant(DEFAULT_DESKTOP_HEIGHT_OFFSET)).toFloat(); + applySensorLocation(); + applyDesktopHeightOffset(); + } + settings.endGroup(); +} + +void LeapMotionPlugin::InputDevice::clearState() { + for (size_t i = 0; i < LeapMotionJointIndex::Size; i++) { + int poseIndex = LeapMotionJointIndexToPoseIndex((LeapMotionJointIndex)i); + _poseStateMap[poseIndex] = controller::Pose(); + } +} + +void LeapMotionPlugin::applySensorLocation() { + bool isLeapOnHMD = _sensorLocation == SENSOR_ON_HMD; + _controller.setPolicyFlags(isLeapOnHMD ? Leap::Controller::POLICY_OPTIMIZE_HMD : Leap::Controller::POLICY_DEFAULT); + _inputDevice->setIsLeapOnHMD(isLeapOnHMD); +} + +void LeapMotionPlugin::applyDesktopHeightOffset() { + _inputDevice->setDektopHeightOffset(_desktopHeightOffset); +} + +const float LEFT_SIDE_SIGN = -1.0f; +const float RIGHT_SIDE_SIGN = 1.0f; + +glm::quat LeapBasisToQuat(float sideSign, const Leap::Matrix& basis) { + glm::vec3 xAxis = sideSign * glm::vec3(basis.xBasis.x, basis.xBasis.y, basis.xBasis.z); + glm::vec3 yAxis = glm::vec3(basis.yBasis.x, basis.yBasis.y, basis.yBasis.z); + glm::vec3 zAxis = glm::vec3(basis.zBasis.x, basis.zBasis.y, basis.zBasis.z); + glm::quat orientation = (glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis))); + return orientation; +} + +glm::vec3 LeapVectorToVec3(const Leap::Vector& vec) { + return glm::vec3(vec.x * METERS_PER_MILLIMETER, vec.y * METERS_PER_MILLIMETER, vec.z * METERS_PER_MILLIMETER); +} + +void LeapMotionPlugin::processFrame(const Leap::Frame& frame) { + // Default to uncontrolled. + for (int i = 0; i < _joints.size(); i++) { + _joints[i].position = glm::vec3(); + } + + auto hands = frame.hands(); + const int MAX_NUMBER_OF_HANDS = 2; + for (int i = 0; i < hands.count() && i < MAX_NUMBER_OF_HANDS; i++) { + auto hand = hands[i]; + + int sideSign = hand.isLeft() ? LEFT_SIDE_SIGN : RIGHT_SIDE_SIGN; + int jointIndex = hand.isLeft() ? LeapMotionJointIndex::LeftHand : LeapMotionJointIndex::RightHand; + + // Hand. + _joints[jointIndex].position = LeapVectorToVec3(hand.wristPosition()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, hand.basis()); + + // Fingers. + // Leap Motion SDK guarantees full set of fingers and finger joints so can straightforwardly process them all. + Leap::FingerList fingers = hand.fingers(); + for (int j = Leap::Finger::Type::TYPE_THUMB; j <= Leap::Finger::Type::TYPE_PINKY; j++) { + Leap::Finger finger; + finger = fingers[j]; + Leap::Bone bone; + bone = finger.bone(Leap::Bone::Type::TYPE_PROXIMAL); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.prevJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + bone = finger.bone(Leap::Bone::Type::TYPE_INTERMEDIATE); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.prevJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + bone = finger.bone(Leap::Bone::Type::TYPE_DISTAL); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.prevJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + jointIndex++; + _joints[jointIndex].position = LeapVectorToVec3(bone.nextJoint()); + _joints[jointIndex].orientation = LeapBasisToQuat(sideSign, bone.basis()); + } + } +} + diff --git a/plugins/hifiLeapMotion/src/LeapMotionPlugin.h b/plugins/hifiLeapMotion/src/LeapMotionPlugin.h new file mode 100644 index 0000000000..391d569567 --- /dev/null +++ b/plugins/hifiLeapMotion/src/LeapMotionPlugin.h @@ -0,0 +1,99 @@ +// +// LeapMotionPlugin.h +// +// Created by David Rowe on 15 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 +// + +#ifndef hifi_LeapMotionPlugin_h +#define hifi_LeapMotionPlugin_h + +#include +#include + +// LeapMotion SDK +#include + +class LeapMotionPlugin : public InputPlugin { + Q_OBJECT + +public: + // InputPlugin methods + virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + + bool isHandController() const override { return true; } + + // Plugin methods + virtual const QString getName() const override { return NAME; } + const QString getID() const override { return LEAPMOTION_ID_STRING; } + + virtual void init() override; + + virtual bool activate() override; + virtual void deactivate() override; + + virtual void saveSettings() const override; + virtual void loadSettings() override; + +protected: + static const char* NAME; + static const char* LEAPMOTION_ID_STRING; + const float DEFAULT_DESKTOP_HEIGHT_OFFSET = 0.2f; + + bool _enabled { false }; + QString _sensorLocation; + float _desktopHeightOffset { DEFAULT_DESKTOP_HEIGHT_OFFSET }; + + struct LeapMotionJoint { + glm::vec3 position; + glm::quat orientation; + }; + + std::vector _joints; + std::vector _prevJoints; + + class InputDevice : public controller::InputDevice { + public: + friend class LeapMotionPlugin; + + InputDevice() : controller::InputDevice("LeapMotion") {} + + // Device functions + virtual controller::Input::NamedVector getAvailableInputs() const override; + virtual QString getDefaultMappingConfig() const override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override {}; + virtual void focusOutEvent() override {}; + + void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, + const std::vector& joints, + const std::vector& prevJoints); + + void clearState(); + + void setDektopHeightOffset(float desktopHeightOffset) { _desktopHeightOffset = desktopHeightOffset; }; + void setIsLeapOnHMD(bool isLeapOnHMD) { _isLeapOnHMD = isLeapOnHMD; }; + + private: + float _desktopHeightOffset { 0.0f }; + bool _isLeapOnHMD { false }; + }; + + std::shared_ptr _inputDevice { std::make_shared() }; + +private: + void applySensorLocation(); + void applyDesktopHeightOffset(); + + void processFrame(const Leap::Frame& frame); + + Leap::Controller _controller; + + bool _hasLeapMotionBeenConnected { false }; + int64_t _lastFrameID { -1 }; +}; + +#endif // hifi_LeapMotionPlugin_h diff --git a/plugins/hifiLeapMotion/src/LeapMotionProvider.cpp b/plugins/hifiLeapMotion/src/LeapMotionProvider.cpp new file mode 100644 index 0000000000..62517439c3 --- /dev/null +++ b/plugins/hifiLeapMotion/src/LeapMotionProvider.cpp @@ -0,0 +1,51 @@ +// +// LeapMotionProvider.cpp +// +// Created by David Rowe on 15 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 +// + +#include + +#include +#include +#include + +#include +#include + +#include "LeapMotionPlugin.h" + +class LeapMotionProvider : public QObject, public InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + LeapMotionProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~LeapMotionProvider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new LeapMotionPlugin()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + + virtual void destroyInputPlugins() override { + _inputPlugins.clear(); + } + +private: + InputPluginList _inputPlugins; +}; + +#include "LeapMotionProvider.moc" diff --git a/plugins/hifiLeapMotion/src/plugin.json b/plugins/hifiLeapMotion/src/plugin.json new file mode 100644 index 0000000000..2e867d96e4 --- /dev/null +++ b/plugins/hifiLeapMotion/src/plugin.json @@ -0,0 +1 @@ +{"name":"Leap Motion"} diff --git a/scripts/system/controllers/leapHands.js b/scripts/system/controllers/leapHands.js deleted file mode 100644 index 1be0b1e5f6..0000000000 --- a/scripts/system/controllers/leapHands.js +++ /dev/null @@ -1,527 +0,0 @@ -// -// leapHands.js -// examples -// -// Created by David Rowe on 8 Sep 2014. -// Copyright 2014 High Fidelity, Inc. -// -// This is an example script that uses the Leap Motion to make the avatar's hands replicate the user's hand actions. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -var leftTriggerValue = 0; -var rightTriggerValue = 0; - -var LEAP_TRIGGER_START_ANGLE = 15.0; -var LEAP_TRIGGER_END_ANGLE = 40.0; - -function getLeapMotionLeftTrigger() { - //print("left trigger = " + leftTriggerValue); - return leftTriggerValue; -} -function getLeapMotionRightTrigger() { - //print("right trigger = " + rightTriggerValue); - return rightTriggerValue; -} - -var leapHands = (function () { - - var isOnHMD, - LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD", - LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip - HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief - hasHandAndWristJoints, - handToWristOffset = [], // For avatars without a wrist joint we control an estimate of a proper hand joint position - HAND_OFFSET = 0.4, // Relative distance of wrist to hand versus wrist to index finger knuckle - handAnimationStateHandlers, - handAnimationStateFunctions, - handAnimationStateProperties, - hands, - wrists, - NUM_HANDS = 2, // 0 = left; 1 = right - fingers, - NUM_FINGERS = 5, // 0 = thumb; ...; 4 = pinky - THUMB = 0, - MIDDLE_FINGER = 2, - NUM_FINGER_JOINTS = 3, // 0 = metacarpal(hand)-proximal(finger) joint; ...; 2 = intermediate-distal joint - MAX_HAND_INACTIVE_COUNT = 20, - calibrationStatus, - UNCALIBRATED = 0, - CALIBRATING = 1, - CALIBRATED = 2, - CALIBRATION_TIME = 1000, // milliseconds - avatarScale, - avatarFaceModelURL, - avatarSkeletonModelURL, - settingsTimer, - HMD_CAMERA_TO_AVATAR_ROTATION = [ - Quat.angleAxis(180.0, { x: 0, y: 0, z: 1 }), - Quat.angleAxis(-180.0, { x: 0, y: 0, z: 1 }) - ], - DESKTOP_CAMERA_TO_AVATAR_ROTATION = - Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 })), - LEAP_THUMB_ROOT_ADJUST = [Quat.fromPitchYawRollDegrees(0, 0, 20), Quat.fromPitchYawRollDegrees(0, 0, -20)]; - - function printSkeletonJointNames() { - var jointNames, - i; - - print(MyAvatar.skeletonModelURL); - - print("Skeleton joint names ..."); - jointNames = MyAvatar.getJointNames(); - for (i = 0; i < jointNames.length; i += 1) { - print(i + ": " + jointNames[i]); - } - print("... skeleton joint names"); - } - - function animateLeftHand() { - var ROTATION_AND_POSITION = 0; - - return { - leftHandType: ROTATION_AND_POSITION, - leftHandPosition: hands[0].position, - leftHandRotation: hands[0].rotation - }; - } - - function animateRightHand() { - var ROTATION_AND_POSITION = 0; - - return { - rightHandType: ROTATION_AND_POSITION, - rightHandPosition: hands[1].position, - rightHandRotation: hands[1].rotation - }; - } - - function finishCalibration() { - var avatarPosition, - handPosition, - middleFingerPosition, - leapHandHeight, - h; - - if (!isOnHMD) { - if (hands[0].controller.isActive() && hands[1].controller.isActive()) { - leapHandHeight = (hands[0].controller.getAbsTranslation().y + hands[1].controller.getAbsTranslation().y) / 2.0; - } else { - calibrationStatus = UNCALIBRATED; - return; - } - } - - avatarPosition = MyAvatar.position; - - for (h = 0; h < NUM_HANDS; h += 1) { - handPosition = MyAvatar.getJointPosition(hands[h].jointName); - if (!hasHandAndWristJoints) { - middleFingerPosition = MyAvatar.getJointPosition(fingers[h][MIDDLE_FINGER][0].jointName); - handToWristOffset[h] = Vec3.multiply(Vec3.subtract(handPosition, middleFingerPosition), 1.0 - HAND_OFFSET); - } - - if (isOnHMD) { - // Offset of Leap Motion origin from physical eye position - hands[h].zeroPosition = { x: 0.0, y: 0.0, z: HMD_OFFSET + LEAP_OFFSET }; - } else { - hands[h].zeroPosition = { - x: handPosition.x - avatarPosition.x, - y: handPosition.y - avatarPosition.y, - z: avatarPosition.z - handPosition.z - }; - hands[h].zeroPosition = Vec3.multiplyQbyV(MyAvatar.orientation, hands[h].zeroPosition); - hands[h].zeroPosition.y = hands[h].zeroPosition.y - leapHandHeight; - } - } - - MyAvatar.clearJointData("LeftHand"); - MyAvatar.clearJointData("LeftForeArm"); - MyAvatar.clearJointData("RightHand"); - MyAvatar.clearJointData("RightForeArm"); - - calibrationStatus = CALIBRATED; - print("Leap Motion: Calibrated"); - } - - function calibrate() { - var jointNames, - i; - - calibrationStatus = CALIBRATING; - - avatarScale = MyAvatar.scale; - avatarFaceModelURL = MyAvatar.faceModelURL; - avatarSkeletonModelURL = MyAvatar.skeletonModelURL; - - // Does this skeleton have both wrist and hand joints? - hasHandAndWristJoints = false; - jointNames = MyAvatar.getJointNames(); - for (i = 0; i < jointNames.length; i += 1) { - hasHandAndWristJoints = hasHandAndWristJoints || jointNames[i].toLowerCase() === "leftwrist"; - } - - // Set avatar arms vertical, forearms horizontal, as "zero" position for calibration - MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, 90.0)); - MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollDegrees(0.0, 90.0, 0.0)); - MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, 0.0, -90.0)); - MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollDegrees(0.0, -90.0, 0.0)); - - // Wait for arms to assume their positions before calculating - Script.setTimeout(finishCalibration, CALIBRATION_TIME); - } - - function checkCalibration() { - - if (calibrationStatus === CALIBRATED) { - return true; - } - - if (calibrationStatus !== CALIBRATING) { - calibrate(); - } - - return false; - } - - function setIsOnHMD() { - isOnHMD = Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM); - print("Leap Motion: " + (isOnHMD ? "Is on HMD" : "Is on desk")); - } - - function checkSettings() { - if (calibrationStatus > UNCALIBRATED && (MyAvatar.scale !== avatarScale - || MyAvatar.faceModelURL !== avatarFaceModelURL - || MyAvatar.skeletonModelURL !== avatarSkeletonModelURL - || Menu.isOptionChecked(LEAP_ON_HMD_MENU_ITEM) !== isOnHMD)) { - print("Leap Motion: Recalibrate..."); - calibrationStatus = UNCALIBRATED; - - setIsOnHMD(); - } - } - - function setUp() { - - wrists = [ - { - jointName: "LeftWrist", - controller: Controller.createInputController("Spatial", "joint_L_wrist") - }, - { - jointName: "RightWrist", - controller: Controller.createInputController("Spatial", "joint_R_wrist") - } - ]; - - hands = [ - { - jointName: "LeftHand", - controller: Controller.createInputController("Spatial", "joint_L_hand"), - inactiveCount: 0 - }, - { - jointName: "RightHand", - controller: Controller.createInputController("Spatial", "joint_R_hand"), - inactiveCount: 0 - } - ]; - - // The Leap controller's first joint is the hand-metacarpal joint but this joint's data is not used because it's too - // dependent on the model skeleton exactly matching the Leap skeleton; using just the second and subsequent joints - // seems to work better over all. - fingers = [{}, {}]; - fingers[0] = [ - [ - { jointName: "LeftHandThumb1", controller: Controller.createInputController("Spatial", "joint_L_thumb2") }, - { jointName: "LeftHandThumb2", controller: Controller.createInputController("Spatial", "joint_L_thumb3") }, - { jointName: "LeftHandThumb3", controller: Controller.createInputController("Spatial", "joint_L_thumb4") } - ], - [ - { jointName: "LeftHandIndex1", controller: Controller.createInputController("Spatial", "joint_L_index2") }, - { jointName: "LeftHandIndex2", controller: Controller.createInputController("Spatial", "joint_L_index3") }, - { jointName: "LeftHandIndex3", controller: Controller.createInputController("Spatial", "joint_L_index4") } - ], - [ - { jointName: "LeftHandMiddle1", controller: Controller.createInputController("Spatial", "joint_L_middle2") }, - { jointName: "LeftHandMiddle2", controller: Controller.createInputController("Spatial", "joint_L_middle3") }, - { jointName: "LeftHandMiddle3", controller: Controller.createInputController("Spatial", "joint_L_middle4") } - ], - [ - { jointName: "LeftHandRing1", controller: Controller.createInputController("Spatial", "joint_L_ring2") }, - { jointName: "LeftHandRing2", controller: Controller.createInputController("Spatial", "joint_L_ring3") }, - { jointName: "LeftHandRing3", controller: Controller.createInputController("Spatial", "joint_L_ring4") } - ], - [ - { jointName: "LeftHandPinky1", controller: Controller.createInputController("Spatial", "joint_L_pinky2") }, - { jointName: "LeftHandPinky2", controller: Controller.createInputController("Spatial", "joint_L_pinky3") }, - { jointName: "LeftHandPinky3", controller: Controller.createInputController("Spatial", "joint_L_pinky4") } - ] - ]; - fingers[1] = [ - [ - { jointName: "RightHandThumb1", controller: Controller.createInputController("Spatial", "joint_R_thumb2") }, - { jointName: "RightHandThumb2", controller: Controller.createInputController("Spatial", "joint_R_thumb3") }, - { jointName: "RightHandThumb3", controller: Controller.createInputController("Spatial", "joint_R_thumb4") } - ], - [ - { jointName: "RightHandIndex1", controller: Controller.createInputController("Spatial", "joint_R_index2") }, - { jointName: "RightHandIndex2", controller: Controller.createInputController("Spatial", "joint_R_index3") }, - { jointName: "RightHandIndex3", controller: Controller.createInputController("Spatial", "joint_R_index4") } - ], - [ - { jointName: "RightHandMiddle1", controller: Controller.createInputController("Spatial", "joint_R_middle2") }, - { jointName: "RightHandMiddle2", controller: Controller.createInputController("Spatial", "joint_R_middle3") }, - { jointName: "RightHandMiddle3", controller: Controller.createInputController("Spatial", "joint_R_middle4") } - ], - [ - { jointName: "RightHandRing1", controller: Controller.createInputController("Spatial", "joint_R_ring2") }, - { jointName: "RightHandRing2", controller: Controller.createInputController("Spatial", "joint_R_ring3") }, - { jointName: "RightHandRing3", controller: Controller.createInputController("Spatial", "joint_R_ring4") } - ], - [ - { jointName: "RightHandPinky1", controller: Controller.createInputController("Spatial", "joint_R_pinky2") }, - { jointName: "RightHandPinky2", controller: Controller.createInputController("Spatial", "joint_R_pinky3") }, - { jointName: "RightHandPinky3", controller: Controller.createInputController("Spatial", "joint_R_pinky4") } - ] - ]; - - handAnimationStateHandlers = [null, null]; - handAnimationStateFunctions = [animateLeftHand, animateRightHand]; - handAnimationStateProperties = [ - ["leftHandType", "leftHandPosition", "leftHandRotation"], - ["rightHandType", "rightHandPosition", "rightHandPosition"] - ]; - - setIsOnHMD(); - - settingsTimer = Script.setInterval(checkSettings, 2000); - - calibrationStatus = UNCALIBRATED; - - { - var mapping = Controller.newMapping("LeapmotionTrigger"); - mapping.from(getLeapMotionLeftTrigger).to(Controller.Standard.LT); - mapping.from(getLeapMotionRightTrigger).to(Controller.Standard.RT); - mapping.enable(); - } - } - - function moveHands() { - var h, - i, - j, - side, - handOffset, - wristOffset, - handRotation, - locRotation, - cameraOrientation, - inverseAvatarOrientation; - - for (h = 0; h < NUM_HANDS; h += 1) { - side = h === 0 ? -1.0 : 1.0; - - if (hands[h].controller.isActive()) { - - // Calibrate if necessary. - if (!checkCalibration()) { - return; - } - - // Hand animation handlers ... - if (handAnimationStateHandlers[h] === null) { - handAnimationStateHandlers[h] = MyAvatar.addAnimationStateHandler(handAnimationStateFunctions[h], - handAnimationStateProperties[h]); - } - - // Hand position ... - handOffset = hands[h].controller.getAbsTranslation(); - handRotation = hands[h].controller.getAbsRotation(); - - if (isOnHMD) { - - // Adjust to control wrist position if "hand" joint is at wrist ... - if (!hasHandAndWristJoints) { - wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]); - handOffset = Vec3.sum(handOffset, wristOffset); - } - - // Hand offset in camera coordinates ... - handOffset = { - x: -handOffset.x, - y: -handOffset.z, - z: -handOffset.y - hands[h].zeroPosition.z - }; - - // Hand offset in world coordinates ... - cameraOrientation = Camera.getOrientation(); - handOffset = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(cameraOrientation, handOffset)); - - // Hand offset in avatar coordinates ... - inverseAvatarOrientation = Quat.inverse(MyAvatar.orientation); - handOffset = Vec3.subtract(handOffset, MyAvatar.position); - handOffset = Vec3.multiplyQbyV(inverseAvatarOrientation, handOffset); - handOffset.z = -handOffset.z; - handOffset.x = -handOffset.x; - - - // Hand rotation in camera coordinates ... - handRotation = { - x: -handRotation.y, - y: -handRotation.z, - z: -handRotation.x, - w: handRotation.w - }; - - // Hand rotation in avatar coordinates ... - handRotation = Quat.multiply(HMD_CAMERA_TO_AVATAR_ROTATION[h], handRotation); - cameraOrientation = { - x: cameraOrientation.z, - y: cameraOrientation.y, - z: cameraOrientation.x, - w: cameraOrientation.w - }; - cameraOrientation = Quat.multiply(cameraOrientation, Quat.inverse(MyAvatar.orientation)); - handRotation = Quat.multiply(handRotation, cameraOrientation); // Works!!! - - } else { - - // Adjust to control wrist position if "hand" joint is at wrist ... - if (!hasHandAndWristJoints) { - wristOffset = Vec3.multiplyQbyV(handRotation, handToWristOffset[h]); - handOffset = Vec3.sum(handOffset, wristOffset); - } - - // Hand offset in camera coordinates ... - handOffset = { - x: -handOffset.x, - y: hands[h].zeroPosition.y + handOffset.y, - z: hands[h].zeroPosition.z - handOffset.z - }; - - // Hand rotation in camera coordinates ... - handRotation = { - x: handRotation.z, - y: handRotation.y, - z: handRotation.x, - w: handRotation.w - }; - - // Hand rotation in avatar coordinates ... - handRotation = Quat.multiply(DESKTOP_CAMERA_TO_AVATAR_ROTATION, handRotation); - } - - // Set hand position and orientation for animation state handler ... - hands[h].position = handOffset; - hands[h].rotation = handRotation; - - // Set finger joints ... - var summed = 0; - var closeAngle = 0; - for (i = 0; i < NUM_FINGERS; i += 1) { - for (j = 0; j < NUM_FINGER_JOINTS; j += 1) { - if (fingers[h][i][j].controller !== null) { - locRotation = fingers[h][i][j].controller.getLocRotation(); - var eulers = Quat.safeEulerAngles(locRotation); - closeAngle += eulers.x; - - summed++; - - if (i === THUMB) { - locRotation = { - x: side * locRotation.y, - y: side * -locRotation.z, - z: side * -locRotation.x, - w: locRotation.w - }; - if (j === 0) { - // Adjust avatar thumb root joint rotation to make avatar hands look better - locRotation = Quat.multiply(LEAP_THUMB_ROOT_ADJUST[h], locRotation); - } - } else { - locRotation = { - x: -locRotation.x, - y: -locRotation.z, - z: -locRotation.y, - w: locRotation.w - }; - } - MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation); - } - } - } - - hands[h].inactiveCount = 0; - if (summed > 0) { - closeAngle /= summed; - } - - var triggerValue = (-closeAngle - LEAP_TRIGGER_START_ANGLE) / (LEAP_TRIGGER_END_ANGLE - LEAP_TRIGGER_START_ANGLE); - triggerValue = Math.max(0.0, Math.min(triggerValue, 1.0)); - - if (h == 0) { - leftTriggerValue = triggerValue; - } else { - rightTriggerValue = triggerValue; - - } - - } else { - - if (hands[h].inactiveCount < MAX_HAND_INACTIVE_COUNT) { - - hands[h].inactiveCount += 1; - - if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) { - if (handAnimationStateHandlers[h] !== null) { - MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]); - handAnimationStateHandlers[h] = null; - leftTriggerValue = 0.0; - rightTriggerValue = 0.0; - } - } - } - } - } - } - - function tearDown() { - var h, - i, - j; - - Script.clearInterval(settingsTimer); - - for (h = 0; h < NUM_HANDS; h += 1) { - Controller.releaseInputController(hands[h].controller); - Controller.releaseInputController(wrists[h].controller); - if (handAnimationStateHandlers[h] !== null) { - MyAvatar.removeAnimationStateHandler(handAnimationStateHandlers[h]); - } - for (i = 0; i < NUM_FINGERS; i += 1) { - for (j = 0; j < NUM_FINGER_JOINTS; j += 1) { - if (fingers[h][i][j].controller !== null) { - Controller.releaseInputController(fingers[h][i][j].controller); - } - } - } - } - } - - return { - printSkeletonJointNames: printSkeletonJointNames, - setUp : setUp, - moveHands : moveHands, - tearDown : tearDown - }; -}()); - - -//leapHands.printSkeletonJointNames(); - -leapHands.setUp(); -Script.update.connect(leapHands.moveHands); -Script.scriptEnding.connect(leapHands.tearDown); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index c60e3a67f7..37618253ee 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -384,7 +384,6 @@ function onButtonClicked() { } else { fillImageDataFromPrevious(); tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); - tablet.webEventReceived.connect(onMessage); HMD.openTablet(); } } @@ -504,8 +503,9 @@ function takeSnapshot() { Window.takeSnapshot(false, includeAnimated, 1.91); }, SNAPSHOT_DELAY); }, FINISH_SOUND_DELAY); + UserActivityLogger.logAction("snaphshot_taken", { location: location.href }); } - + function isDomainOpen(id, callback) { print("Checking open status of domain with ID:", id); var status = false; @@ -659,10 +659,15 @@ function maybeDeleteSnapshotStories() { storyIDsToMaybeDelete = []; } function onTabletScreenChanged(type, url) { + var wasInSnapshotReview = isInSnapshotReview; isInSnapshotReview = (type === "Web" && url === SNAPSHOT_REVIEW_URL); button.editProperties({ isActive: isInSnapshotReview }); - if (!isInSnapshotReview) { - tablet.webEventReceived.disconnect(onMessage); + if (isInSnapshotReview !== wasInSnapshotReview) { + if (isInSnapshotReview) { + tablet.webEventReceived.connect(onMessage); + } else { + tablet.webEventReceived.disconnect(onMessage); + } } } function onUsernameChanged() { diff --git a/server-console/src/main.js b/server-console/src/main.js index 95fb0d81b2..efa04a8512 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -334,6 +334,7 @@ function startInterface(url) { // create a new Interface instance - Interface makes sure only one is running at a time var pInterface = new Process('interface', interfacePath, argArray); + pInterface.detached = true; pInterface.start(); } @@ -892,10 +893,19 @@ function onContentLoaded() { deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); if (dsPath && acPath) { - domainServer = new Process('domain-server', dsPath, ["--get-temp-name"], logPath); - acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n7', - '--log-directory', logPath, - '--http-status-port', httpStatusPort], httpStatusPort, logPath); + var dsArguments = ['--get-temp-name', + '--parent-pid', process.pid]; + domainServer = new Process('domain-server', dsPath, dsArguments, logPath); + domainServer.restartOnCrash = true; + + var acArguments = ['-n7', + '--log-directory', logPath, + '--http-status-port', httpStatusPort, + '--parent-pid', process.pid]; + acMonitor = new ACMonitorProcess('ac-monitor', acPath, acArguments, + httpStatusPort, logPath); + acMonitor.restartOnCrash = true; + homeServer = new ProcessGroup('home', [domainServer, acMonitor]); logWindow = new LogWindow(acMonitor, domainServer); diff --git a/server-console/src/modules/hf-process.js b/server-console/src/modules/hf-process.js index 20b8966148..767befec7b 100644 --- a/server-console/src/modules/hf-process.js +++ b/server-console/src/modules/hf-process.js @@ -113,6 +113,10 @@ function Process(name, command, commandArgs, logDirectory) { this.logDirectory = logDirectory; this.logStdout = null; this.logStderr = null; + this.detached = false; + this.restartOnCrash = false; + this.restartCount = 0; + this.firstRestartTimestamp = Date.now(); this.state = ProcessStates.STOPPED; }; @@ -165,9 +169,10 @@ Process.prototype = extend(Process.prototype, { try { this.child = childProcess.spawn(this.command, this.commandArgs, { - detached: false, + detached: this.detached, stdio: ['ignore', logStdout, logStderr] }); + log.debug("Spawned " + this.command + " with pid " + this.child.pid); } catch (e) { log.debug("Got error starting child process for " + this.name, e); this.child = null; @@ -266,7 +271,30 @@ Process.prototype = extend(Process.prototype, { clearTimeout(this.stoppingTimeoutID); this.stoppingTimeoutID = null; } + // Grab current state before updating it. + var unexpectedShutdown = this.state != ProcessStates.STOPPING; this.updateState(ProcessStates.STOPPED); + + if (unexpectedShutdown && this.restartOnCrash) { + var MAX_RESTARTS = 10; + var MAX_RESTARTS_PERIOD = 10; // 10 min + var MSEC_PER_MIN = 1000 * 60; + var now = Date.now(); + var timeDiff = (now - this.firstRestartTimestamp) / MSEC_PER_MIN; + if (timeDiff > MAX_RESTARTS_PERIOD) { + this.firstRestartTimestamp = now; + this.restartCount = 0; + } + + if (this.restartCount < 10) { + this.restartCount++; + + log.warn("Child stopped unexpectedly, restarting."); + this.start(); + } else { + log.warn("Child stopped unexpectedly too many times, not restarting."); + } + } } });