mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 16:34:17 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into vive-in-desktop
This commit is contained in:
commit
6f30cb1825
64 changed files with 1339 additions and 1582 deletions
|
@ -9,8 +9,12 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QThread>
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <LogHandler.h>
|
||||
#include <SharedUtil.h>
|
||||
|
@ -20,10 +24,6 @@
|
|||
#include "Assignment.h"
|
||||
#include "AssignmentClient.h"
|
||||
#include "AssignmentClientMonitor.h"
|
||||
#include "AssignmentClientApp.h"
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
||||
|
||||
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<LimitedNodeList, NodeList>();
|
||||
|
|
|
@ -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<NodeList>()->getLocalSockAddr().getPort()));
|
||||
|
||||
_childArguments.append("--" + PARENT_PID_OPTION);
|
||||
_childArguments.append(QString::number(QCoreApplication::applicationPid()));
|
||||
|
||||
QString nowString, stdoutFilenameTemp, stderrFilenameTemp, stdoutPathTemp, stderrPathTemp;
|
||||
|
||||
|
||||
|
|
|
@ -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<AvatarMixerClientData*>(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();
|
||||
|
|
|
@ -320,14 +320,18 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
++numOtherAvatars;
|
||||
|
||||
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
cmake/macros/TargetLeapMotion.cmake
Normal file
12
cmake/macros/TargetLeapMotion.cmake
Normal file
|
@ -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()
|
|
@ -171,7 +171,7 @@ void DomainMetadata::maybeUpdateUsers() {
|
|||
if (linkedData) {
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(linkedData);
|
||||
|
||||
if (!nodeData->wasAssigned()) {
|
||||
if (!nodeData->wasAssigned() && node->getType() == NodeType::Agent) {
|
||||
++numConnected;
|
||||
|
||||
if (nodeData->getUsername().isEmpty()) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
8
interface/external/leapmotion/readme.txt
vendored
8
interface/external/leapmotion/readme.txt
vendored
|
@ -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.
|
||||
2. Clear your build directory, run cmake and build, and you should be all set.
|
||||
|
|
48
interface/resources/controllers/leapmotion.json
Normal file
48
interface/resources/controllers/leapmotion.json
Normal file
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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" },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NodeList>();
|
||||
|
||||
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<NodeList>()->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));
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QQmlContext>
|
||||
#include <QList>
|
||||
|
||||
#include <Application.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
@ -24,6 +25,8 @@
|
|||
#include "AvatarBookmarks.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
#include "QVariantGLM.h"
|
||||
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
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<QAction*>(sender());
|
||||
const QString& address = action->data().toString();
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->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<QString, QVariant> 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<QVariant>& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList<QVariant>()).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<AvatarManager>()->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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<QAction*> 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<OffscreenUi>();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<QAction*> actions, const QString& name) const;
|
||||
|
||||
|
||||
bool contains(const QString& name) const;
|
||||
|
||||
QVariantMap _bookmarks; // { name: url, ... }
|
||||
QPointer<MenuWrapper> _bookmarksMenu;
|
||||
QPointer<QAction> _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();
|
||||
|
|
|
@ -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<AddressManager>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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<int, std::pair<controller::Pose, QString>> 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<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
ThreadSafeValueCache<FingerPosesMap> _leftHandFingerPosesInSensorFramceCache { };
|
||||
ThreadSafeValueCache<FingerPosesMap> _rightHandFingerPosesInSensorFramceCache { };
|
||||
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _hipsControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _spine2ControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _headControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _leftArmControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache { controller::Pose() };
|
||||
|
||||
bool _hmdLeanRecenterEnabled = true;
|
||||
AnimPose _prePhysicsRoomPose;
|
||||
|
|
|
@ -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<MyAvatar*>(_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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define hifi_MySkeletonModel_h
|
||||
|
||||
#include <avatars-renderer/SkeletonModel.h>
|
||||
#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
|
||||
|
|
|
@ -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 <NumericalConstants.h>
|
||||
|
||||
#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
|
||||
}
|
|
@ -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 <QDateTime>
|
||||
|
||||
#include <trackers/MotionTracker.h>
|
||||
|
||||
#ifdef HAVE_LEAPMOTION
|
||||
#include <Leap.h>
|
||||
#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
|
|
@ -17,7 +17,6 @@
|
|||
#include <plugins/PluginManager.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include <trackers/MotionTracker.h>
|
||||
|
||||
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<InputController>(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)); }
|
||||
|
||||
|
|
|
@ -25,38 +25,6 @@
|
|||
#include <WheelEvent.h>
|
||||
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<InputKey, controller::InputController::Pointer>;
|
||||
InputControllerMap _inputControllers;
|
||||
};
|
||||
|
||||
const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
|
||||
|
|
|
@ -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<JointData> 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);
|
||||
}
|
||||
|
|
|
@ -627,6 +627,7 @@ public:
|
|||
void markIdentityDataChanged() { _identityDataChanged = true; }
|
||||
|
||||
void pushIdentitySequenceNumber() { ++_identitySequenceNumber; };
|
||||
bool hasProcessedFirstIdentity() const { return _hasProcessedFirstIdentity; }
|
||||
|
||||
float getDensity() const { return _density; }
|
||||
|
||||
|
|
|
@ -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"),
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
|
||||
BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(CameraCorrection), nullptr )) };
|
||||
|
||||
State::Data _stateCache{ State::DEFAULT };
|
||||
State::Signature _stateSignatureCache { 0 };
|
||||
|
@ -409,6 +416,8 @@ protected:
|
|||
|
||||
PipelineStageState() {
|
||||
_cameraCorrectionBuffer.edit<CameraCorrection>() = CameraCorrection();
|
||||
_cameraCorrectionBufferIdentity.edit<CameraCorrection>() = CameraCorrection();
|
||||
_cameraCorrectionBufferIdentity._buffer->flush();
|
||||
}
|
||||
} _pipeline;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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<gpu::Context> _context;
|
||||
std::shared_ptr<gpu::Framebuffer> _blitFramebuffer;
|
||||
std::shared_ptr<render::ShapePipeline> _pipeline;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
}
|
|
@ -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 <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// 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 <typename T>
|
||||
TemplateSingleton<T> TemplateSingleton<T>::_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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 <GLMHelpers.h>
|
||||
|
||||
/// 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
|
|
@ -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
|
||||
|
|
15
plugins/hifiLeapMotion/CMakeLists.txt
Normal file
15
plugins/hifiLeapMotion/CMakeLists.txt
Normal file
|
@ -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()
|
493
plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp
Normal file
493
plugins/hifiLeapMotion/src/LeapMotionPlugin.cpp
Normal file
|
@ -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 <QLoggingCategory>
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <PathUtils.h>
|
||||
#include <Preferences.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
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<controller::UserInputMapper>();
|
||||
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<LeapMotionJointIndex>(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<LeapMotionPlugin::LeapMotionJoint>& joints,
|
||||
const std::vector<LeapMotionPlugin::LeapMotionJoint>& 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<Preferences>();
|
||||
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<controller::UserInputMapper>();
|
||||
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<controller::UserInputMapper>();
|
||||
userInputMapper->registerDevice(_inputDevice);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LeapMotionPlugin::deactivate() {
|
||||
if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) {
|
||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
99
plugins/hifiLeapMotion/src/LeapMotionPlugin.h
Normal file
99
plugins/hifiLeapMotion/src/LeapMotionPlugin.h
Normal file
|
@ -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 <controllers/InputDevice.h>
|
||||
#include <plugins/InputPlugin.h>
|
||||
|
||||
// LeapMotion SDK
|
||||
#include <Leap.h>
|
||||
|
||||
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<LeapMotionJoint> _joints;
|
||||
std::vector<LeapMotionJoint> _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<LeapMotionPlugin::LeapMotionJoint>& joints,
|
||||
const std::vector<LeapMotionPlugin::LeapMotionJoint>& 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> _inputDevice { std::make_shared<InputDevice>() };
|
||||
|
||||
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
|
51
plugins/hifiLeapMotion/src/LeapMotionProvider.cpp
Normal file
51
plugins/hifiLeapMotion/src/LeapMotionProvider.cpp
Normal file
|
@ -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 <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QtPlugin>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#include <plugins/RuntimePlugin.h>
|
||||
#include <plugins/InputPlugin.h>
|
||||
|
||||
#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"
|
1
plugins/hifiLeapMotion/src/plugin.json
Normal file
1
plugins/hifiLeapMotion/src/plugin.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"Leap Motion"}
|
|
@ -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);
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue