Merge branch 'master' into 20269

This commit is contained in:
David Rowe 2015-01-15 11:01:17 -08:00
commit 5195ae2424
13 changed files with 1168 additions and 5 deletions

View file

@ -9,8 +9,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
Currently building on Windows has been tested using the following compilers:
* Visual Studio 2013
(If anyone can test using Visual Studio 2013 Express then please update this document)
* Visual Studio 2013 Express
####Visual Studio 2013

View file

@ -0,0 +1,33 @@
# Try to find the RSSDK library
#
# You must provide a RSSDK_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# RSSDK_FOUND - system found RSSDK
# RSSDK_INCLUDE_DIRS - the RSSDK include directory
# RSSDK_LIBRARIES - Link this to use RSSDK
#
# Created on 12/7/2014 by Thijs Wenker
# Copyright (c) 2014 High Fidelity
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("rssdk")
find_path(RSSDK_INCLUDE_DIRS pxcbase.h PATH_SUFFIXES include HINTS ${RSSDK_SEARCH_DIRS})
if (WIN32)
find_library(RSSDK_LIBRARY_DEBUG libpxc_d PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
find_library(RSSDK_LIBRARY_RELEASE libpxc PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
endif ()
include(SelectLibraryConfigurations)
select_library_configurations(RSSDK)
set(RSSDK_LIBRARIES "${RSSDK_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(RSSDK DEFAULT_MSG RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES)
mark_as_advanced(RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES RSSDK_SEARCH_DIRS)

525
examples/realsenseHands.js Normal file
View file

@ -0,0 +1,525 @@
//
// realsenseHands.js
// examples
//
// Created by Thijs Wenker on 18 Dec 2014.
// Copyright 2014 High Fidelity, Inc.
//
// This is an example script that uses the Intel RealSense to make the avatar's hands replicate the user's hand actions.
// Most of this script is copied from leapHands.js
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var leapHands = (function () {
var isOnHMD,
MotionTracker = "RealSense",
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
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;
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");
/*
http://public.highfidelity.io/models/skeletons/ron_standing.fst
Skeleton joint names ...
0: Hips
1: RightUpLeg
2: RightLeg
3: RightFoot
4: RightToeBase
5: RightToe_End
6: LeftUpLeg
7: LeftLeg
8: LeftFoot
9: LeftToeBase
10: LeftToe_End
11: Spine
12: Spine1
13: Spine2
14: RightShoulder
15: RightArm
16: RightForeArm
17: RightHand
18: RightHandPinky1
19: RightHandPinky2
20: RightHandPinky3
21: RightHandPinky4
22: RightHandRing1
23: RightHandRing2
24: RightHandRing3
25: RightHandRing4
26: RightHandMiddle1
27: RightHandMiddle2
28: RightHandMiddle3
29: RightHandMiddle4
30: RightHandIndex1
31: RightHandIndex2
32: RightHandIndex3
33: RightHandIndex4
34: RightHandThumb1
35: RightHandThumb2
36: RightHandThumb3
37: RightHandThumb4
38: LeftShoulder
39: LeftArm
40: LeftForeArm
41: LeftHand
42: LeftHandPinky1
43: LeftHandPinky2
44: LeftHandPinky3
45: LeftHandPinky4
46: LeftHandRing1
47: LeftHandRing2
48: LeftHandRing3
49: LeftHandRing4
50: LeftHandMiddle1
51: LeftHandMiddle2
52: LeftHandMiddle3
53: LeftHandMiddle4
54: LeftHandIndex1
55: LeftHandIndex2
56: LeftHandIndex3
57: LeftHandIndex4
58: LeftHandThumb1
59: LeftHandThumb2
60: LeftHandThumb3
61: LeftHandThumb4
62: Neck
63: Head
64: HeadTop_End
65: body
... skeleton joint names
*/
}
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("LeftArm");
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
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.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0));
MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 90.0));
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.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(MotionTracker, "joint_L_wrist")
},
{
jointName: "RightWrist",
controller: Controller.createInputController(MotionTracker, "joint_R_wrist")
}
];
hands = [
{
jointName: "LeftHand",
controller: Controller.createInputController(MotionTracker, "joint_L_hand"),
inactiveCount: 0
},
{
jointName: "RightHand",
controller: Controller.createInputController(MotionTracker, "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(MotionTracker, "joint_L_thumb2") },
{ jointName: "LeftHandThumb2", controller: Controller.createInputController(MotionTracker, "joint_L_thumb3") },
{ jointName: "LeftHandThumb3", controller: Controller.createInputController(MotionTracker, "joint_L_thumb4") }
],
[
{ jointName: "LeftHandIndex1", controller: Controller.createInputController(MotionTracker, "joint_L_index2") },
{ jointName: "LeftHandIndex2", controller: Controller.createInputController(MotionTracker, "joint_L_index3") },
{ jointName: "LeftHandIndex3", controller: Controller.createInputController(MotionTracker, "joint_L_index4") }
],
[
{ jointName: "LeftHandMiddle1", controller: Controller.createInputController(MotionTracker, "joint_L_middle2") },
{ jointName: "LeftHandMiddle2", controller: Controller.createInputController(MotionTracker, "joint_L_middle3") },
{ jointName: "LeftHandMiddle3", controller: Controller.createInputController(MotionTracker, "joint_L_middle4") }
],
[
{ jointName: "LeftHandRing1", controller: Controller.createInputController(MotionTracker, "joint_L_ring2") },
{ jointName: "LeftHandRing2", controller: Controller.createInputController(MotionTracker, "joint_L_ring3") },
{ jointName: "LeftHandRing3", controller: Controller.createInputController(MotionTracker, "joint_L_ring4") }
],
[
{ jointName: "LeftHandPinky1", controller: Controller.createInputController(MotionTracker, "joint_L_pinky2") },
{ jointName: "LeftHandPinky2", controller: Controller.createInputController(MotionTracker, "joint_L_pinky3") },
{ jointName: "LeftHandPinky3", controller: Controller.createInputController(MotionTracker, "joint_L_pinky4") }
]
];
fingers[1] = [
[
{ jointName: "RightHandThumb1", controller: Controller.createInputController(MotionTracker, "joint_R_thumb2") },
{ jointName: "RightHandThumb2", controller: Controller.createInputController(MotionTracker, "joint_R_thumb3") },
{ jointName: "RightHandThumb3", controller: Controller.createInputController(MotionTracker, "joint_R_thumb4") }
],
[
{ jointName: "RightHandIndex1", controller: Controller.createInputController(MotionTracker, "joint_R_index2") },
{ jointName: "RightHandIndex2", controller: Controller.createInputController(MotionTracker, "joint_R_index3") },
{ jointName: "RightHandIndex3", controller: Controller.createInputController(MotionTracker, "joint_R_index4") }
],
[
{ jointName: "RightHandMiddle1", controller: Controller.createInputController(MotionTracker, "joint_R_middle2") },
{ jointName: "RightHandMiddle2", controller: Controller.createInputController(MotionTracker, "joint_R_middle3") },
{ jointName: "RightHandMiddle3", controller: Controller.createInputController(MotionTracker, "joint_R_middle4") }
],
[
{ jointName: "RightHandRing1", controller: Controller.createInputController(MotionTracker, "joint_R_ring2") },
{ jointName: "RightHandRing2", controller: Controller.createInputController(MotionTracker, "joint_R_ring3") },
{ jointName: "RightHandRing3", controller: Controller.createInputController(MotionTracker, "joint_R_ring4") }
],
[
{ jointName: "RightHandPinky1", controller: Controller.createInputController(MotionTracker, "joint_R_pinky2") },
{ jointName: "RightHandPinky2", controller: Controller.createInputController(MotionTracker, "joint_R_pinky3") },
{ jointName: "RightHandPinky3", controller: Controller.createInputController(MotionTracker, "joint_R_pinky4") }
]
];
setIsOnHMD();
settingsTimer = Script.setInterval(checkSettings, 2000);
calibrationStatus = UNCALIBRATED;
}
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 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: hands[h].zeroPosition.x - handOffset.x,
y: hands[h].zeroPosition.y - handOffset.z,
z: hands[h].zeroPosition.z + handOffset.y
};
handOffset.z = -handOffset.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.z,
y: handRotation.y,
z: handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
if (h === 0) {
handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation);
} else {
handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation);
}
cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation);
handRotation = Quat.multiply(inverseAvatarOrientation, handRotation);
} else {
// 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.y,
y: handRotation.z,
z: -handRotation.x,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
if (h === 0) {
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 1, y: 0, z: 0 }),
handRotation);
} else {
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 1, y: 0, z: 0 }),
handRotation);
}
}
// Set hand position and orientation ...
MyAvatar.setJointModelPositionAndOrientation(hands[h].jointName, handOffset, handRotation, true);
// Set finger joints ...
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();
if (i === THUMB) {
locRotation = {
x: side * locRotation.y,
y: side * -locRotation.z,
z: side * -locRotation.x,
w: locRotation.w
};
} else {
locRotation = {
x: -locRotation.x,
y: -locRotation.z,
z: -locRotation.y,
w: locRotation.w
};
}
MyAvatar.setJointData(fingers[h][i][j].jointName, locRotation);
}
}
}
hands[h].inactiveCount = 0;
} else {
if (hands[h].inactiveCount < MAX_HAND_INACTIVE_COUNT) {
hands[h].inactiveCount += 1;
if (hands[h].inactiveCount === MAX_HAND_INACTIVE_COUNT) {
if (h === 0) {
MyAvatar.clearJointData("LeftHand");
MyAvatar.clearJointData("LeftForeArm");
MyAvatar.clearJointData("LeftArm");
} else {
MyAvatar.clearJointData("RightHand");
MyAvatar.clearJointData("RightForeArm");
MyAvatar.clearJointData("RightArm");
}
}
}
}
}
}
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);
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);

View file

@ -2,7 +2,7 @@ set(TARGET_NAME interface)
project(${TARGET_NAME})
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "LeapMotion" "RtMidi" "Qxmpp" "SDL2" "Gverb")
set(OPTIONAL_EXTERNALS "Faceshift" "LibOVR" "PrioVR" "Sixense" "LeapMotion" "RtMidi" "Qxmpp" "SDL2" "Gverb" "RSSDK")
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)

9
interface/external/rssdk/readme.txt vendored Normal file
View file

@ -0,0 +1,9 @@
Instructions for adding the Intel Realsense (RSSDK) to Interface
Thijs Wenker, December 19, 2014
This is Windows only for now.
1. Download the SDK at https://software.intel.com/en-us/intel-realsense-sdk/download
2. Copy the `lib` and `include` folder inside this directory

View file

@ -91,6 +91,7 @@
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "devices/Leapmotion.h"
#include "devices/RealSense.h"
#include "devices/MIDIManager.h"
#include "devices/OculusManager.h"
#include "devices/TV3DManager.h"
@ -256,6 +257,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion());
_bookmarks = new Bookmarks(); // Before setting up the menu
// call Menu getInstance static method to set up the menu
_window->setMenuBar(Menu::getInstance());
@ -1685,6 +1688,7 @@ void Application::init() {
DependencyManager::get<Visage>()->init();
Leapmotion::init();
RealSense::init();
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
@ -2135,12 +2139,17 @@ void Application::updateMyAvatar(float deltaTime) {
_myAvatar->update(deltaTime);
{
quint64 now = usecTimestampNow();
quint64 dt = now - _lastSendAvatarDataTime;
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS) {
// send head/hand data to the avatar mixer and voxel server
PerformanceTimer perfTimer("send");
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
packet.append(_myAvatar->toByteArray());
controlledBroadcastToNodes(packet, NodeSet() << NodeType::AvatarMixer);
_lastSendAvatarDataTime = now;
}
}

View file

@ -38,6 +38,7 @@
#include <ViewFrustum.h>
#include "Audio.h"
#include "Bookmarks.h"
#include "Camera.h"
#include "DatagramProcessor.h"
#include "Environment.h"
@ -111,6 +112,10 @@ static const float MIRROR_REARVIEW_DISTANCE = 0.722f;
static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.56f;
static const float MIRROR_FIELD_OF_VIEW = 30.0f;
// 70 times per second - target is 60hz, but this helps account for any small deviations
// in the update loop
static const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = (1000 * 1000) / 70;
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
static const QString INFO_HELP_PATH = "html/interface-welcome-allsvg.html";
@ -296,6 +301,8 @@ public:
QRect getDesirableApplicationGeometry();
RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; }
Bookmarks* getBookmarks() const { return _bookmarks; }
signals:
/// Fired when we're simulating; allows external parties to hook in.
@ -567,9 +574,13 @@ private:
quint64 _lastNackTime;
quint64 _lastSendDownstreamAudioStats;
quint64 _lastSendAvatarDataTime;
bool _isVSyncOn;
bool _aboutToQuit;
Bookmarks* _bookmarks;
};
#endif // hifi_Application_h

View file

@ -0,0 +1,73 @@
//
// Bookmarks.cpp
// interface/src
//
// Created by David Rowe on 13 Jan 2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QFile>
#include <QJsonDocument>
#include <QStandardPaths>
#include "Bookmarks.h"
Bookmarks::Bookmarks() {
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME;
readFromFile();
}
void Bookmarks::insert(const QString& name, const QString& address) {
_bookmarks.insert(name, address);
if (contains(name)) {
qDebug() << "Added bookmark:" << name << "," << address;
persistToFile();
} else {
qWarning() << "Couldn't add bookmark: " << name << "," << address;
}
}
void Bookmarks::remove(const QString& name) {
_bookmarks.remove(name);
if (!contains(name)) {
qDebug() << "Deleted bookmark:" << name;
persistToFile();
} else {
qWarning() << "Couldn't delete bookmark:" << name;
}
}
bool Bookmarks::contains(const QString& name) const {
return _bookmarks.contains(name);
}
void Bookmarks::readFromFile() {
QFile loadFile(_bookmarksFilename);
if (!loadFile.open(QIODevice::ReadOnly)) {
qWarning("Couldn't open bookmarks file for reading");
return;
}
QByteArray data = loadFile.readAll();
QJsonDocument json(QJsonDocument::fromJson(data));
_bookmarks = json.object().toVariantMap();
}
void Bookmarks::persistToFile() {
QFile saveFile(_bookmarksFilename);
if (!saveFile.open(QIODevice::WriteOnly)) {
qWarning("Couldn't open bookmarks file for writing");
return;
}
QJsonDocument json(QJsonObject::fromVariantMap(_bookmarks));
QByteArray data = json.toJson();
saveFile.write(data);
}

41
interface/src/Bookmarks.h Normal file
View file

@ -0,0 +1,41 @@
//
// Bookmarks.h
// interface/src
//
// Created by David Rowe on 13 Jan 2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_Bookmarks_h
#define hifi_Bookmarks_h
#include <QJsonObject>
#include <QDebug>
#include <QMap>
#include <QObject>
class Bookmarks: public QObject {
Q_OBJECT
public:
Bookmarks();
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
void remove(const QString& name);
bool contains(const QString& name) const;
QVariantMap* getBookmarks() { return &_bookmarks; };
private:
QVariantMap _bookmarks; // { name: address, ... }
const QString BOOKMARKS_FILENAME = "bookmarks.json";
QString _bookmarksFilename;
void readFromFile();
void persistToFile();
};
#endif // hifi_Bookmarks_h

View file

@ -44,6 +44,7 @@
#include "Audio.h"
#include "audio/AudioIOStatsRenderer.h"
#include "audio/AudioScope.h"
#include "devices/RealSense.h"
#include "devices/Visage.h"
#include "Menu.h"
#include "scripting/LocationScriptingInterface.h"
@ -124,6 +125,15 @@ Menu::Menu() :
appInstance, SLOT(toggleRunningScriptsWidget()));
addDisabledActionAndSeparator(fileMenu, "Location");
addActionToQMenuAndActionHash(fileMenu, MenuOption::BookmarkLocation, 0,
this, SLOT(bookmarkLocation()));
_bookmarksMenu = fileMenu->addMenu(MenuOption::Bookmarks);
_bookmarksMenu->setEnabled(false);
_deleteBookmarksMenu = addActionToQMenuAndActionHash(fileMenu,
MenuOption::DeleteBookmark, 0,
this, SLOT(deleteBookmark()));
_deleteBookmarksMenu->setEnabled(false);
loadBookmarks();
addActionToQMenuAndActionHash(fileMenu,
MenuOption::NameLocation,
Qt::CTRL | Qt::Key_N,
@ -266,7 +276,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
avatar, SLOT(updateMotionBehavior()));
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With...");
QMenu* collisionsMenu = avatarMenu->addMenu("Collide With");
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false,
avatar, SLOT(onToggleRagdoll()));
addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithAvatars,
@ -444,6 +454,11 @@ Menu::Menu() :
QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
#ifdef HAVE_RSSDK
QMenu* realSenseOptionsMenu = handOptionsMenu->addMenu("RealSense");
addActionToQMenuAndActionHash(realSenseOptionsMenu, MenuOption::LoadRSSDKFile, 0, this, SLOT(loadRSSDKFile()));
#endif
QMenu* networkMenu = developerMenu->addMenu("Network");
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
addCheckableActionToQMenuAndActionHash(networkMenu,
@ -1029,6 +1044,125 @@ void Menu::changeVSync() {
Application::getInstance()->setVSyncEnabled(isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn));
}
void Menu::loadBookmarks() {
QVariantMap* bookmarks = Application::getInstance()->getBookmarks()->getBookmarks();
if (bookmarks->count() > 0) {
QMapIterator<QString, QVariant> i(*bookmarks);
while (i.hasNext()) {
i.next();
QString bookmarkName = i.key();
QString bookmarkAddress = i.value().toString();
QAction* teleportAction = new QAction(getMenu(MenuOption::Bookmarks));
teleportAction->setData(bookmarkAddress);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, bookmarkName, 0, QAction::NoRole);
}
_bookmarksMenu->setEnabled(true);
_deleteBookmarksMenu->setEnabled(true);
}
}
void Menu::bookmarkLocation() {
QInputDialog bookmarkLocationDialog(Application::getInstance()->getWindow());
bookmarkLocationDialog.setWindowTitle("Bookmark Location");
bookmarkLocationDialog.setLabelText("Name:");
bookmarkLocationDialog.setInputMode(QInputDialog::TextInput);
bookmarkLocationDialog.resize(400, 200);
if (bookmarkLocationDialog.exec() == QDialog::Rejected) {
return;
}
QString bookmarkName = bookmarkLocationDialog.textValue().trimmed();
bookmarkName = bookmarkName.replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
if (bookmarkName.length() == 0) {
return;
}
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks* bookmarks = Application::getInstance()->getBookmarks();
if (bookmarks->contains(bookmarkName)) {
QMessageBox duplicateBookmarkMessage;
duplicateBookmarkMessage.setIcon(QMessageBox::Warning);
duplicateBookmarkMessage.setText("The bookmark name you entered already exists in your list.");
duplicateBookmarkMessage.setInformativeText("Would you like to overwrite it?");
duplicateBookmarkMessage.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
duplicateBookmarkMessage.setDefaultButton(QMessageBox::Yes);
if (duplicateBookmarkMessage.exec() == QMessageBox::No) {
return;
}
removeAction(_bookmarksMenu, bookmarkName);
}
QAction* teleportAction = new QAction(getMenu(MenuOption::Bookmarks));
teleportAction->setData(bookmarkAddress);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
QList<QAction*> menuItems = _bookmarksMenu->actions();
int position = 0;
while (position < menuItems.count() && bookmarkName > menuItems[position]->text()) {
position += 1;
}
addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, bookmarkName, 0,
QAction::NoRole, position);
bookmarks->insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
_bookmarksMenu->setEnabled(true);
_deleteBookmarksMenu->setEnabled(true);
}
void Menu::teleportToBookmark() {
QAction *action = qobject_cast<QAction *>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void Menu::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> menuItems = _bookmarksMenu->actions();
for (int i = 0; i < menuItems.count(); i += 1) {
bookmarkList.append(menuItems[i]->text());
}
QInputDialog deleteBookmarkDialog(Application::getInstance()->getWindow());
deleteBookmarkDialog.setWindowTitle("Delete Bookmark");
deleteBookmarkDialog.setLabelText("Select the bookmark to delete");
deleteBookmarkDialog.resize(400, 400);
deleteBookmarkDialog.setOption(QInputDialog::UseListViewForComboBoxItems);
deleteBookmarkDialog.setComboBoxItems(bookmarkList);
deleteBookmarkDialog.setOkButtonText("Delete");
if (deleteBookmarkDialog.exec() == QDialog::Rejected) {
return;
}
QString bookmarkName = deleteBookmarkDialog.textValue().trimmed();
if (bookmarkName.length() == 0) {
return;
}
removeAction(_bookmarksMenu, bookmarkName);
Bookmarks* bookmarks = Application::getInstance()->getBookmarks();
bookmarks->remove(bookmarkName);
if (_bookmarksMenu->actions().count() == 0) {
_bookmarksMenu->setEnabled(false);
_deleteBookmarksMenu->setEnabled(false);
}
}
void Menu::displayNameLocationResponse(const QString& errorString) {
if (!errorString.isEmpty()) {
@ -1169,6 +1303,10 @@ void Menu::showChat() {
}
}
void Menu::loadRSSDKFile() {
RealSense::getInstance()->loadRSSDKFile();
}
void Menu::toggleChat() {
#ifdef HAVE_QXMPP
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());

View file

@ -202,6 +202,9 @@ private slots:
void editAttachments();
void editAnimations();
void changePrivateKey();
void bookmarkLocation();
void teleportToBookmark();
void deleteBookmark();
void nameLocation();
void toggleLocationList();
void hmdToolsClosed();
@ -216,6 +219,7 @@ private slots:
void audioMuteToggled();
void displayNameLocationResponse(const QString& errorString);
void changeVSync();
void loadRSSDKFile();
private:
static Menu* _instance;
@ -309,6 +313,10 @@ private:
bool _shouldRenderTableNeedsRebuilding = true;
QMap<float, float> _shouldRenderTable;
void loadBookmarks();
QMenu* _bookmarksMenu;
QAction* _deleteBookmarksMenu;
};
namespace MenuOption {
@ -336,6 +344,8 @@ namespace MenuOption {
const QString Bandwidth = "Bandwidth Display";
const QString BandwidthDetails = "Bandwidth Details";
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
const QString BookmarkLocation = "Bookmark Location";
const QString Bookmarks = "Bookmarks";
const QString CascadedShadows = "Cascaded";
const QString CachesSize = "Caches Size";
const QString Chat = "Chat...";
@ -348,6 +358,7 @@ namespace MenuOption {
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString ControlWithSpeech = "Control With Speech";
const QString DeleteBookmark = "Delete Bookmark...";
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
const QString DontDoPrecisionPicking = "Don't Do Precision Picking";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
@ -391,6 +402,7 @@ namespace MenuOption {
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 LoadRSSDKFile = "Load .rssdk file";
const QString LodTools = "LOD Tools";
const QString Login = "Login";
const QString Log = "Log";

View file

@ -0,0 +1,250 @@
//
// RealSense.cpp
// interface/src/devices
//
// Created by Thijs Wenker on 12/10/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 "RealSense.h"
#include "MainWindow.h"
#include "Menu.h"
#include "SharedUtil.h"
const int PALMROOT_NUM_JOINTS = 2;
const int FINGER_NUM_JOINTS = 4;
const DeviceTracker::Name RealSense::NAME = "RealSense";
// 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..1] <=> [ hand, forearm]
MotionTracker::Index evalRealSenseJointIndex(bool isRightSide, int finger, int bone) {
#ifdef HAVE_RSSDK
MotionTracker::Index offset = 1 // start after root
+ (int(isRightSide) * PXCHandData::NUMBER_OF_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;
}
#else
return -1;
#endif // HAVE_RSSDK
}
// static
void RealSense::init() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new RealSense and register it
RealSense* realSense = new RealSense();
DeviceTracker::registerDevice(NAME, realSense);
}
}
// static
RealSense* RealSense::getInstance() {
DeviceTracker* device = DeviceTracker::getDevice(NAME);
if (!device) {
// create a new RealSense and register it
RealSense* realSense = new RealSense();
DeviceTracker::registerDevice(NAME, realSense);
}
return dynamic_cast< RealSense* > (device);
}
RealSense::RealSense() :
MotionTracker(),
_active(false)
{
#ifdef HAVE_RSSDK
_handData = NULL;
_session = PXCSession_Create();
initSession(false, NULL);
// Create the RealSense joint hierarchy
std::vector< Semantic > sides;
sides.push_back("joint_L_");
sides.push_back("joint_R_");
std::vector< Semantic > rootBones;
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);
}
}
}
#endif // HAVE_RSSDK
}
RealSense::~RealSense() {
#ifdef HAVE_RSSDK
_manager->Release();
#endif // HAVE_RSSDK
}
void RealSense::initSession(bool playback, QString filename) {
#ifdef HAVE_RSSDK
_active = false;
_properlyInitialized = false;
if (_handData != NULL) {
_handData->Release();
_handController->Release();
_session->Release();
_config->Release();
}
_manager = _session->CreateSenseManager();
if (playback) {
_manager->QueryCaptureManager()->SetFileName(filename.toStdWString().c_str(), false);
}
_manager->QueryCaptureManager()->SetRealtime(!playback);
_manager->EnableHand(0);
_handController = _manager->QueryHand();
if (_manager->Init() == PXC_STATUS_NO_ERROR) {
_handData = _handController->CreateOutput();
PXCCapture::Device *device = _manager->QueryCaptureManager()->QueryDevice();
PXCCapture::DeviceInfo dinfo;
_manager->QueryCaptureManager()->QueryDevice()->QueryDeviceInfo(&dinfo);
if (dinfo.model == PXCCapture::DEVICE_MODEL_IVCAM)
{
device->SetDepthConfidenceThreshold(1);
device->SetMirrorMode(PXCCapture::Device::MIRROR_MODE_DISABLED);
device->SetIVCAMFilterOption(6);
}
_properlyInitialized = true;
}
_config = _handController->CreateActiveConfiguration();
_config->EnableStabilizer(true);
_config->SetTrackingMode(PXCHandData::TRACKING_MODE_FULL_HAND);
_config->ApplyChanges();
#endif // HAVE_RSSDK
}
#ifdef HAVE_RSSDK
glm::quat quatFromPXCPoint4DF32(const PXCPoint4DF32& basis) {
return glm::normalize(glm::quat(basis.w, basis.x, basis.y, basis.z) * glm::quat(glm::vec3(0, M_PI, 0)));
}
glm::vec3 vec3FromPXCPoint3DF32(const PXCPoint3DF32& vec) {
return glm::vec3(-vec.x, vec.y, -vec.z);
}
#endif // HAVE_RSSDK
void RealSense::update() {
#ifdef HAVE_RSSDK
bool wasActive = _active;
_active = _manager->IsConnected() && _properlyInitialized;
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;
}
pxcStatus sts = _manager->AcquireFrame(true);
_handData->Update();
PXCHandData::JointData nodes[2][PXCHandData::NUMBER_OF_JOINTS] = {};
PXCHandData::ExtremityData extremitiesPointsNodes[2][PXCHandData::NUMBER_OF_EXTREMITIES] = {};
for (pxcI32 i = 0; i < _handData->QueryNumberOfHands(); i++) {
PXCHandData::IHand* handData;
if (_handData->QueryHandData(PXCHandData::ACCESS_ORDER_BY_TIME, i, handData) == PXC_STATUS_NO_ERROR) {
int rightSide = handData->QueryBodySide() == PXCHandData::BODY_SIDE_RIGHT;
PXCHandData::JointData jointData;
JointTracker* parentJointTracker = _jointsArray.data();
//Iterate Joints
int rootBranchIndex = -1;
JointTracker* palmJoint = NULL;
for (int j = 0; j < PXCHandData::NUMBER_OF_JOINTS; j++) {
handData->QueryTrackedJoint((PXCHandData::JointType)j, jointData);
nodes[i][j] = jointData;
if (j == PXCHandData::JOINT_WRIST) {
JointTracker* wrist = editJointTracker(evalRealSenseJointIndex(rightSide, rootBranchIndex, 1)); // 1 is the index of the wrist joint
wrist->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
wrist->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
wrist->updateLocFromAbsTransform(parentJointTracker);
wrist->activeFrame();
parentJointTracker = wrist;
continue;
} else if (j == PXCHandData::JOINT_CENTER) {
palmJoint = editJointTracker(evalRealSenseJointIndex(rightSide, rootBranchIndex, 0)); // 0 is the index of the palm joint
palmJoint->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
palmJoint->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
palmJoint->updateLocFromAbsTransform(parentJointTracker);
palmJoint->activeFrame();
parentJointTracker = palmJoint;
continue;
}
int finger_index = j - PALMROOT_NUM_JOINTS;
int finger = finger_index / FINGER_NUM_JOINTS;
int finger_bone = finger_index % FINGER_NUM_JOINTS;
JointTracker* ljointTracker = editJointTracker(evalRealSenseJointIndex(rightSide, finger, finger_bone));
if (jointData.confidence > 0) {
ljointTracker->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
ljointTracker->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
ljointTracker->activeFrame();
}
if (finger_bone == (FINGER_NUM_JOINTS - 1)) {
parentJointTracker = palmJoint;
continue;
}
parentJointTracker = ljointTracker;
}
}
}
_manager->ReleaseFrame();
#endif // HAVE_RSSDK
}
void RealSense::loadRSSDKFile() {
QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation));
QString fileNameString = QFileDialog::getOpenFileName(Application::getInstance()->getWindow(), tr("Open RSSDK clip"),
locationDir,
tr("RSSDK Recordings (*.rssdk)"));
if (!fileNameString.isEmpty()) {
initSession(true, fileNameString);
}
}

View file

@ -0,0 +1,63 @@
//
// RealSense.h
// interface/src/devices
//
// Created by Thijs Wenker on 12/10/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_RealSense_h
#define hifi_RealSense_h
#include <QFileDialog>
#include "MotionTracker.h"
#ifdef HAVE_RSSDK
#include <pxcsession.h>
#include <pxchandmodule.h>
#include <pxchandconfiguration.h>
#include <pxcsensemanager.h>
#include <pxchanddata.h>
#endif
/// Handles interaction with the RealSense skeleton tracking suit.
class RealSense : public QObject, public MotionTracker {
Q_OBJECT
public:
static const Name NAME;
static void init();
/// RealSense MotionTracker factory
static RealSense* getInstance();
bool isActive() const { return _active; }
virtual void update();
void loadRSSDKFile();
protected:
RealSense();
virtual ~RealSense();
void initSession(bool playback, QString filename);
private:
#ifdef HAVE_RSSDK
PXCSession* _session;
PXCSenseManager* _manager;
PXCHandModule* _handController;
PXCHandData* _handData;
PXCHandConfiguration* _config;
#endif
bool _properlyInitialized;
bool _active;
};
#endif // hifi_RealSense_h