From 70d155048fb81763b8df70de7e71517d23b886e9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 27 Jul 2015 14:43:32 -0700 Subject: [PATCH 01/41] Add SMI iViewHMD eye tracking as an optional library --- cmake/modules/FindiViewHMD.cmake | 27 ++++++++++++++++++++++++++ interface/CMakeLists.txt | 2 +- interface/external/iViewHMD/readme.txt | 14 +++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 cmake/modules/FindiViewHMD.cmake create mode 100644 interface/external/iViewHMD/readme.txt diff --git a/cmake/modules/FindiViewHMD.cmake b/cmake/modules/FindiViewHMD.cmake new file mode 100644 index 0000000000..eb107a2833 --- /dev/null +++ b/cmake/modules/FindiViewHMD.cmake @@ -0,0 +1,27 @@ +# +# FindiViewHMD.cmake +# +# Try to find the SMI iViewHMD eye tracker library +# +# You must provide a IVIEWHMD_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# IVIEWHMD_FOUND - system found iViewHMD +# IVIEWHMD_INCLUDE_DIRS - the iViewHMD include directory +# IVIEWHMD_LIBRARIES - link this to use iViewHMD +# +# Created on 27 Jul 2015 by David Rowe +# Copyright 2015 High Fidelity, Inc. +# + +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("iViewHMD") + +find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) +find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES lib HINTS ${IVIEWHMD_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(iViewHMD DEFAULT_MSG IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES) + +mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0c44ac801f..46a83ea707 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -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" "Sixense" "LeapMotion" "RtMidi" "SDL2" "RSSDK") +set(OPTIONAL_EXTERNALS "Faceshift" "Sixense" "LeapMotion" "RtMidi" "SDL2" "RSSDK" "iViewHMD") foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) diff --git a/interface/external/iViewHMD/readme.txt b/interface/external/iViewHMD/readme.txt new file mode 100644 index 0000000000..7e0af7e23e --- /dev/null +++ b/interface/external/iViewHMD/readme.txt @@ -0,0 +1,14 @@ + +Instructions for adding SMI HMD Eye Tracking to Interface on Windows +David Rowe, 27 Jul 2015. + +1. Download and install the SMI HMD Eye Tracking software from http://update.smivision.com/iViewNG-HMD.exe. + +2. Copy the SDK folders (3rdParty, include, lib) from the SDK installation folder C:\Program Files (x86)\SMI\iViewNG-HMD\SDK + into the interface/externals/iViewHMD folder. This readme.txt should be there as well. + + 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, set the ENV variable "HIFI_LIB_DIR" to a directory containing a subfolder + "iViewHMD" that contains the folders mentioned above. + +3. Clear your build directory, run cmake and build, and you should be all set. From e0ca6eb5edb1c8acd4a1cc1b1c77b6dce98a9934 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 27 Jul 2015 15:33:15 -0700 Subject: [PATCH 02/41] Add eye tracker class and menu item --- interface/src/Application.cpp | 22 +++++++++++++++++---- interface/src/Application.h | 2 ++ interface/src/Menu.cpp | 7 +++++++ interface/src/Menu.h | 1 + interface/src/devices/EyeTracker.cpp | 21 ++++++++++++++++++++ interface/src/devices/EyeTracker.h | 29 ++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 interface/src/devices/EyeTracker.cpp create mode 100644 interface/src/devices/EyeTracker.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bb564824b0..1c7b2ff2df 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -108,12 +108,13 @@ #include "audio/AudioScope.h" #include "devices/DdeFaceTracker.h" +#include "devices/EyeTracker.h" #include "devices/Faceshift.h" #include "devices/Leapmotion.h" -#include "devices/RealSense.h" -#include "devices/SDL2Manager.h" #include "devices/MIDIManager.h" #include "devices/OculusManager.h" +#include "devices/RealSense.h" +#include "devices/SDL2Manager.h" #include "devices/TV3DManager.h" #include "scripting/AccountScriptingInterface.h" @@ -261,14 +262,14 @@ bool setupEssentials(int& argc, char** argv) { auto scriptCache = DependencyManager::set(); auto soundCache = DependencyManager::set(); auto faceshift = DependencyManager::set(); + auto ddeFaceTracker = DependencyManager::set(); + auto eyeTracker = DependencyManager::set(); auto audio = DependencyManager::set(); auto audioScope = DependencyManager::set(); auto deferredLightingEffect = DependencyManager::set(); auto textureCache = DependencyManager::set(); auto framebufferCache = DependencyManager::set(); - auto animationCache = DependencyManager::set(); - auto ddeFaceTracker = DependencyManager::set(); auto modelBlender = DependencyManager::set(); auto avatarManager = DependencyManager::set(); auto lodManager = DependencyManager::set(); @@ -635,6 +636,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif +#ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + eyeTracker->init(); +#endif + auto applicationUpdater = DependencyManager::get(); connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); @@ -2007,6 +2013,13 @@ void Application::setActiveFaceTracker() { #endif } +void Application::setActiveEyeTracker() { +#ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + eyeTracker->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking)); +#endif +} + bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { QVector entities; @@ -3408,6 +3421,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi void Application::resetSensors() { DependencyManager::get()->reset(); DependencyManager::get()->reset(); + DependencyManager::get()->reset(); OculusManager::reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index d1886862d2..881d93cab2 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -406,6 +406,8 @@ public slots: void resetSensors(); void setActiveFaceTracker(); + void setActiveEyeTracker(); + void aboutApp(); void showEditEntitiesHelp(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 91ae6a4d02..c5a92747e9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -437,6 +437,13 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false); #endif +#ifdef HAVE_IVIEWHMD + MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking"); + QAction* smiEyeTracking = addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false, + qApp, SLOT(setActiveEyeTracker())); + smiEyeTracking->setVisible(true); +#endif + auto avatarManager = DependencyManager::get(); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false, avatarManager.data(), SLOT(setShouldShowReceiveStats(bool))); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index bf0f89abb5..b8ac2ef189 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -273,6 +273,7 @@ namespace MenuOption { const QString SixenseEnabled = "Enable Hydra Support"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; + const QString SMIEyeTracking = "SMI Eye Tracking"; const QString Stars = "Stars"; const QString Stats = "Stats"; const QString StopAllScripts = "Stop All Scripts"; diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp new file mode 100644 index 0000000000..3e0d9b78dd --- /dev/null +++ b/interface/src/devices/EyeTracker.cpp @@ -0,0 +1,21 @@ +// +// EyeTracker.cpp +// interface/src/devices +// +// Created by David Rowe on 27 Jul 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 "EyeTracker.h" + +void EyeTracker::init() { +} + +void EyeTracker::setEnabled(bool enabled) { +} + +void EyeTracker::reset() { +} diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h new file mode 100644 index 0000000000..a5529770a9 --- /dev/null +++ b/interface/src/devices/EyeTracker.h @@ -0,0 +1,29 @@ +// +// EyeTracker.h +// interface/src/devices +// +// Created by David Rowe on 27 Jul 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_EyeTracker_h +#define hifi_EyeTracker_h + +#include + +#include + +class EyeTracker : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public slots: + void init(); + void setEnabled(bool enabled); + void reset(); +}; + +#endif // hifi_EyeTracker_h From 72a590e34cce4116618840c33499fe3d7f2f57fc Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Jul 2015 11:02:56 -0700 Subject: [PATCH 03/41] Initialize, start, and stop eye tracking with simulated data --- interface/src/Application.cpp | 14 +++--- interface/src/devices/EyeTracker.cpp | 65 ++++++++++++++++++++++++++++ interface/src/devices/EyeTracker.h | 12 +++++ 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1c7b2ff2df..217b8f334b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -595,6 +595,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : loadScripts(); } +#ifdef HAVE_IVIEWHMD + // Do this before loading settings + auto eyeTracker = DependencyManager::get(); + eyeTracker->init(); +#endif + loadSettings(); int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings); @@ -636,11 +642,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif -#ifdef HAVE_IVIEWHMD - auto eyeTracker = DependencyManager::get(); - eyeTracker->init(); -#endif - auto applicationUpdater = DependencyManager::get(); connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); @@ -703,6 +704,9 @@ void Application::cleanupBeforeQuit() { #ifdef HAVE_DDE DependencyManager::destroy(); #endif +#ifdef HAVE_IVIEWHMD + DependencyManager::destroy(); +#endif } void Application::emptyLocalCache() { diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 3e0d9b78dd..ce6afc1d97 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -11,10 +11,75 @@ #include "EyeTracker.h" +#include "InterfaceLogging.h" + +#ifdef HAVE_IVIEWHMD +static void CALLBACK eyeTrackerCallback(smi_CallbackDataStruct* data) { + auto eyeTracker = DependencyManager::get(); + if (eyeTracker) { // Guard against a few callbacks that continue to be received after smi_quit(). + eyeTracker->processData(data); + } +} +#endif + +EyeTracker::~EyeTracker() { +#ifdef HAVE_IVIEWHMD + int result = smi_quit(); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error terminating tracking:" << result; + } +#endif +} + +void EyeTracker::processData(smi_CallbackDataStruct* data) { + if (!_isEnabled) { + return; + } + +#ifdef HAVE_IVIEWHMD + if (data->type == SMI_SIMPLE_GAZE_SAMPLE) { + smi_SampleHMDStruct* sample = (smi_SampleHMDStruct*)data->result; + } +#endif +} + void EyeTracker::init() { + if (_isInitialized) { + qCWarning(interfaceapp) << "Eye Tracker: Already initialized"; + return; + } + +#ifdef HAVE_IVIEWHMD + int result = smi_setCallback(eyeTrackerCallback); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << result; + } else { + _isInitialized = true; + } +#endif } void EyeTracker::setEnabled(bool enabled) { + if (!_isInitialized) { + qCWarning(interfaceapp) << "Eye Tracker: Not initialized before setting enabled"; + return; + } + +#ifdef HAVE_IVIEWHMD + qCDebug(interfaceapp) << "Eye Tracker: Set enabled =" << enabled; + + if (enabled && !_isStreaming) { + // There is no smi_stopStreaming() method so start streaming a maximum of once per program run. + int result = smi_startStreaming(true); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << result; + } else { + _isStreaming = true; + } + } + + _isEnabled = enabled && _isStreaming; +#endif } void EyeTracker::reset() { diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index a5529770a9..1410da3669 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -15,15 +15,27 @@ #include #include +#include + class EyeTracker : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY +public: + ~EyeTracker(); + + void processData(smi_CallbackDataStruct* data); + public slots: void init(); void setEnabled(bool enabled); void reset(); + +private: + bool _isInitialized = false; + bool _isStreaming = false; + bool _isEnabled = false; }; #endif // hifi_EyeTracker_h From f0ed8d8cefdf4cdf327e497e2d9628b1b1a9bee9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Jul 2015 14:07:25 -0700 Subject: [PATCH 04/41] Add menu item that enables / disables simulated eye tracking --- interface/src/Application.cpp | 15 ++++++++------- interface/src/Menu.cpp | 5 +++-- interface/src/Menu.h | 1 + interface/src/devices/EyeTracker.cpp | 21 ++++++++++----------- interface/src/devices/EyeTracker.h | 2 +- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 217b8f334b..fcbe64cb06 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -595,12 +595,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : loadScripts(); } -#ifdef HAVE_IVIEWHMD - // Do this before loading settings - auto eyeTracker = DependencyManager::get(); - eyeTracker->init(); -#endif - loadSettings(); int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings); @@ -642,6 +636,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled); #endif +#ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + eyeTracker->init(); + setActiveEyeTracker(); +#endif + auto applicationUpdater = DependencyManager::get(); connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog); applicationUpdater->checkForUpdate(); @@ -2020,7 +2020,8 @@ void Application::setActiveFaceTracker() { void Application::setActiveEyeTracker() { #ifdef HAVE_IVIEWHMD auto eyeTracker = DependencyManager::get(); - eyeTracker->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking)); + eyeTracker->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking), + Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking)); #endif } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c5a92747e9..fed7b84791 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -439,9 +439,10 @@ Menu::Menu() { #ifdef HAVE_IVIEWHMD MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking"); - QAction* smiEyeTracking = addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false, + addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false, + qApp, SLOT(setActiveEyeTracker())); + addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SimulateEyeTracking, 0, false, qApp, SLOT(setActiveEyeTracker())); - smiEyeTracking->setVisible(true); #endif auto avatarManager = DependencyManager::get(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b8ac2ef189..c1f7bcf751 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -273,6 +273,7 @@ namespace MenuOption { const QString SixenseEnabled = "Enable Hydra Support"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; + const QString SimulateEyeTracking = "Simulate"; const QString SMIEyeTracking = "SMI Eye Tracking"; const QString Stars = "Stars"; const QString Stats = "Stats"; diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index ce6afc1d97..7f0dcb4510 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -59,26 +59,25 @@ void EyeTracker::init() { #endif } -void EyeTracker::setEnabled(bool enabled) { +void EyeTracker::setEnabled(bool enabled, bool simulate) { if (!_isInitialized) { - qCWarning(interfaceapp) << "Eye Tracker: Not initialized before setting enabled"; return; } #ifdef HAVE_IVIEWHMD - qCDebug(interfaceapp) << "Eye Tracker: Set enabled =" << enabled; - - if (enabled && !_isStreaming) { - // There is no smi_stopStreaming() method so start streaming a maximum of once per program run. - int result = smi_startStreaming(true); + qCDebug(interfaceapp) << "Eye Tracker: Set enabled =" << enabled << ", simulate =" << simulate; + bool success = true; + int result = 0; + if (enabled) { + // There is no smi_stopStreaming() method so keep streaming once started in case tracking is re-enabled after stopping. + result = smi_startStreaming(simulate); if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << result; - } else { - _isStreaming = true; + qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); + success = false; } } - _isEnabled = enabled && _isStreaming; + _isEnabled = enabled && success; #endif } diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 1410da3669..ab8a071dd3 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -29,7 +29,7 @@ public: public slots: void init(); - void setEnabled(bool enabled); + void setEnabled(bool enabled, bool simulate); void reset(); private: From 8f0663841c9be4a4d0cd54220ef2bf97ba4ccb03 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Jul 2015 14:09:19 -0700 Subject: [PATCH 05/41] Provide more user-friendly eye tracking error messages --- interface/src/devices/EyeTracker.cpp | 38 ++++++++++++++++++++++++++-- interface/src/devices/EyeTracker.h | 2 ++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 7f0dcb4510..04e6364e33 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -11,6 +11,8 @@ #include "EyeTracker.h" +#include + #include "InterfaceLogging.h" #ifdef HAVE_IVIEWHMD @@ -26,7 +28,7 @@ EyeTracker::~EyeTracker() { #ifdef HAVE_IVIEWHMD int result = smi_quit(); if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error terminating tracking:" << result; + qCWarning(interfaceapp) << "Eye Tracker: Error terminating tracking:" << smiReturnValueToString(result); } #endif } @@ -52,7 +54,8 @@ void EyeTracker::init() { #ifdef HAVE_IVIEWHMD int result = smi_setCallback(eyeTrackerCallback); if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << result; + qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result); + QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } else { _isInitialized = true; } @@ -78,8 +81,39 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { } _isEnabled = enabled && success; + + if (!success) { + // Display error dialog after updating _isEnabled. + QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + } #endif } void EyeTracker::reset() { } + +QString EyeTracker::smiReturnValueToString(int value) { + switch (value) + { + case smi_ErrorReturnValue::SMI_ERROR_NO_CALLBACK_SET: + return "No callback set"; + case smi_ErrorReturnValue::SMI_ERROR_CONNECTING_TO_HMD: + return "Error connecting to HMD"; + case smi_ErrorReturnValue::SMI_ERROR_HMD_NOT_SUPPORTED: + return "HMD not supported"; + case smi_ErrorReturnValue::SMI_ERROR_NOT_IMPLEMENTED: + return "Not implmented"; + case smi_ErrorReturnValue::SMI_ERROR_INVALID_PARAMETER: + return "Invalid parameter"; + case smi_ErrorReturnValue::SMI_ERROR_EYECAMERAS_NOT_AVAILABLE: + return "Eye cameras not available"; + case smi_ErrorReturnValue::SMI_ERROR_OCULUS_RUNTIME_NOT_SUPPORTED: + return "Oculus runtime not supported"; + case smi_ErrorReturnValue::SMI_ERROR_UNKNOWN: + return "Unknown error"; + default: + QString number; + number.setNum(value); + return number; + } +} diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index ab8a071dd3..31c6648712 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -33,6 +33,8 @@ public slots: void reset(); private: + QString smiReturnValueToString(int value); + bool _isInitialized = false; bool _isStreaming = false; bool _isEnabled = false; From 1ae920368ddcd5e717b6f1f5472d6bebd7c08993 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Jul 2015 08:38:20 -0700 Subject: [PATCH 06/41] Calculate and look at the point the eye tracker user is looking at --- interface/src/Application.cpp | 15 ++++++--- interface/src/devices/EyeTracker.cpp | 49 ++++++++++++++++++++++++++++ interface/src/devices/EyeTracker.h | 9 ++++- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fcbe64cb06..5861adc704 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2282,7 +2282,8 @@ void Application::updateMyAvatarLookAtPosition() { PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); _myAvatar->updateLookAtTargetAvatar(); - FaceTracker* tracker = getActiveFaceTracker(); + FaceTracker* faceTracker = getActiveFaceTracker(); + auto eyeTracker = DependencyManager::get(); bool isLookingAtSomeone = false; glm::vec3 lookAtSpot; @@ -2294,6 +2295,10 @@ void Application::updateMyAvatarLookAtPosition() { } else { lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition(); } + } else if (eyeTracker->isTracking()) { + // Look at the point that the user is looking at. + lookAtSpot = _myAvatar->getHead()->getEyePosition() + + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition()); } else { AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock(); if (lookingAt && _myAvatar != lookingAt.get()) { @@ -2331,12 +2336,12 @@ void Application::updateMyAvatarLookAtPosition() { } // Deflect the eyes a bit to match the detected gaze from the face tracker if active. - if (tracker && !tracker->isMuted()) { - float eyePitch = tracker->getEstimatedEyePitch(); - float eyeYaw = tracker->getEstimatedEyeYaw(); + if (faceTracker && !faceTracker->isMuted()) { + float eyePitch = faceTracker->getEstimatedEyePitch(); + float eyeYaw = faceTracker->getEstimatedEyeYaw(); const float GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT = 0.1f; glm::vec3 origin = _myAvatar->getHead()->getEyePosition(); - float deflection = tracker->getEyeDeflection(); + float deflection = faceTracker->getEyeDeflection(); if (isLookingAtSomeone) { deflection *= GAZE_DEFLECTION_REDUCTION_DURING_EYE_CONTACT; } diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 04e6364e33..f13dd0f61a 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -14,6 +14,7 @@ #include #include "InterfaceLogging.h" +#include "OctreeConstants.h" #ifdef HAVE_IVIEWHMD static void CALLBACK eyeTrackerCallback(smi_CallbackDataStruct* data) { @@ -40,7 +41,55 @@ void EyeTracker::processData(smi_CallbackDataStruct* data) { #ifdef HAVE_IVIEWHMD if (data->type == SMI_SIMPLE_GAZE_SAMPLE) { + // Calculate the intersections of the left and right eye look-at vectors with a vertical plane along the monocular + // gaze direction. Average these positions to give the look-at point. + // If the eyes are parallel or diverged, gaze at a distant look-at point calculated the same as for non eye tracking. + // Line-plane intersection: https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection + smi_SampleHMDStruct* sample = (smi_SampleHMDStruct*)data->result; + // The iViewHMD coordinate system has x and z axes reversed compared to Interface, i.e., wearing the HMD: + // - x is left + // - y is up + // - z is forwards + + // Plane + smi_Vec3d point = sample->gazeBasePoint; // mm + smi_Vec3d direction = sample->gazeDirection; + glm::vec3 planePoint = glm::vec3(-point.x, point.y, -point.z) / 1000.0f; + glm::vec3 planeNormal = glm::vec3(-direction.z, 0.0f, direction.x); + glm::vec3 monocularDirection = glm::vec3(-direction.x, direction.y, -direction.z); + + // Left eye + point = sample->left.gazeBasePoint; // mm + direction = sample->left.gazeDirection; + glm::vec3 leftLinePoint = glm::vec3(-point.x, point.y, -point.z) / 1000.0f; + glm::vec3 leftLineDirection = glm::vec3(-direction.x, direction.y, -direction.z); + + // Right eye + point = sample->right.gazeBasePoint; // mm + direction = sample->right.gazeDirection; + glm::vec3 rightLinePoint = glm::vec3(-point.x, point.y, -point.z) / 1000.0f; + glm::vec3 rightLineDirection = glm::vec3(-direction.x, direction.y, -direction.z); + + // Plane - line dot products + float leftLinePlaneDotProduct = glm::dot(leftLineDirection, planeNormal); + float rightLinePlaneDotProduct = glm::dot(rightLineDirection, planeNormal); + + // Gaze into distance if eyes are parallel or diverged; otherwise the look-at is the average of look-at points + if (abs(leftLinePlaneDotProduct) <= FLT_EPSILON || abs(rightLinePlaneDotProduct) <= FLT_EPSILON) { + _lookAtPosition = monocularDirection * (float)TREE_SCALE; + } else { + float leftDistance = glm::dot(planePoint - leftLinePoint, planeNormal) / leftLinePlaneDotProduct; + float rightDistance = glm::dot(planePoint - rightLinePoint, planeNormal) / rightLinePlaneDotProduct; + if (leftDistance <= 0.0f || rightDistance <= 0.0f + || leftDistance > (float)TREE_SCALE || rightDistance > (float)TREE_SCALE) { + _lookAtPosition = monocularDirection * (float)TREE_SCALE; + } else { + glm::vec3 leftIntersectionPoint = leftLinePoint + leftDistance * leftLineDirection; + glm::vec3 rightIntersectionPoint = rightLinePoint + rightDistance * rightLineDirection; + _lookAtPosition = (leftIntersectionPoint + rightIntersectionPoint) / 2.0f; + } + } } #endif } diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 31c6648712..cb970d5be5 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -14,6 +14,8 @@ #include +#include + #include #include @@ -24,6 +26,10 @@ class EyeTracker : public QObject, public Dependency { public: ~EyeTracker(); + + bool isTracking() { return _isEnabled; } + + glm::vec3 getLookAtPosition() { return _lookAtPosition; } // From mid eye point in head frame. void processData(smi_CallbackDataStruct* data); @@ -36,8 +42,9 @@ private: QString smiReturnValueToString(int value); bool _isInitialized = false; - bool _isStreaming = false; bool _isEnabled = false; + + glm::vec3 _lookAtPosition; }; #endif // hifi_EyeTracker_h From 76fe0c876421d6c0256fe602e67f88ab3c63d8f3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Jul 2015 11:14:00 -0700 Subject: [PATCH 07/41] Control eyes only if in HMD view or simulating --- interface/src/Application.cpp | 2 +- interface/src/devices/EyeTracker.cpp | 1 + interface/src/devices/EyeTracker.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5861adc704..02056a94dc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2295,7 +2295,7 @@ void Application::updateMyAvatarLookAtPosition() { } else { lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition(); } - } else if (eyeTracker->isTracking()) { + } else if (eyeTracker->isTracking() && (isHMDMode() || eyeTracker->isSimulating())) { // Look at the point that the user is looking at. lookAtSpot = _myAvatar->getHead()->getEyePosition() + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition()); diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index f13dd0f61a..9e2737d156 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -130,6 +130,7 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { } _isEnabled = enabled && success; + _isSimulating = _isEnabled && simulate; if (!success) { // Display error dialog after updating _isEnabled. diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index cb970d5be5..c41d529e91 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -28,6 +28,7 @@ public: ~EyeTracker(); bool isTracking() { return _isEnabled; } + bool isSimulating() { return _isSimulating; } glm::vec3 getLookAtPosition() { return _lookAtPosition; } // From mid eye point in head frame. @@ -43,6 +44,7 @@ private: bool _isInitialized = false; bool _isEnabled = false; + bool _isSimulating = false; glm::vec3 _lookAtPosition; }; From e5d2a7898001088ed06eae2034fb432965b1cb61 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Jul 2015 11:33:21 -0700 Subject: [PATCH 08/41] Don't check SMI Eye Tracker menu item if tracking doesn't start --- interface/src/Application.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 02056a94dc..90edcc0b54 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2019,9 +2019,12 @@ void Application::setActiveFaceTracker() { void Application::setActiveEyeTracker() { #ifdef HAVE_IVIEWHMD + bool isEyeTrackingOptionChecked = Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking); auto eyeTracker = DependencyManager::get(); - eyeTracker->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking), - Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking)); + eyeTracker->setEnabled(isEyeTrackingOptionChecked, Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking)); + if (isEyeTrackingOptionChecked && !eyeTracker->isTracking()) { + Menu::getInstance()->setIsOptionChecked(MenuOption::SMIEyeTracking, false); + } #endif } From f2ae7bfac0ad9461d67a74627afe6e26b6edafe2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Jul 2015 15:14:43 -0700 Subject: [PATCH 09/41] Don't apply saccades to tracked eyes --- interface/src/avatar/Head.cpp | 7 ++++++- interface/src/devices/EyeTracker.cpp | 1 + libraries/avatars/src/AvatarData.cpp | 6 ++++++ libraries/avatars/src/AvatarData.h | 8 ++++---- libraries/avatars/src/HeadData.cpp | 1 + libraries/avatars/src/HeadData.h | 1 + 6 files changed, 19 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 55f33f57a4..5232fe93c4 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -17,11 +17,13 @@ #include "Application.h" #include "Avatar.h" +#include "DependencyManager.h" #include "GeometryUtil.h" #include "Head.h" #include "Menu.h" #include "Util.h" #include "devices/DdeFaceTracker.h" +#include "devices/EyeTracker.h" #include "devices/Faceshift.h" using namespace std; @@ -116,6 +118,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { applyEyelidOffset(getFinalOrientationInWorldFrame()); } } + + auto eyeTracker = DependencyManager::get(); + _isEyeTrackerConnected = eyeTracker->isTracking(); } // Twist the upper body to follow the rotation of the head, but only do this with my avatar, // since everyone else will see the full joint rotations for other people. @@ -125,7 +130,7 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE); } - if (!(_isFaceTrackerConnected || billboard)) { + if (!(_isFaceTrackerConnected || _isEyeTrackerConnected || billboard)) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; const float AVERAGE_SACCADE_INTERVAL = 6.0f; diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 9e2737d156..a384202255 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -140,6 +140,7 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { } void EyeTracker::reset() { + // Nothing to do. } QString EyeTracker::smiReturnValueToString(int value) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5b970a95a3..295b8ba8d7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -194,6 +194,11 @@ QByteArray AvatarData::toByteArray() { if (_headData->_isFaceTrackerConnected) { setAtBit(bitItems, IS_FACESHIFT_CONNECTED); } + // eye tracker state + if (_headData->_isEyeTrackerConnected) { + setAtBit(bitItems, IS_EYE_TRACKER_CONNECTED); + } + // referential state if (_referential != NULL && _referential->isValid()) { setAtBit(bitItems, HAS_REFERENTIAL); } @@ -431,6 +436,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { + (oneAtBit(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0); _headData->_isFaceTrackerConnected = oneAtBit(bitItems, IS_FACESHIFT_CONNECTED); + _headData->_isEyeTrackerConnected = oneAtBit(bitItems, IS_EYE_TRACKER_CONNECTED); bool hasReferential = oneAtBit(bitItems, HAS_REFERENTIAL); // Referential diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a020be0f7a..7d60555abc 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -77,21 +77,21 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; -// Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of +// Bitset of state flags - we store the key state, hand state, Faceshift, eye tracking, and existence of // referential data in this bit set. The hand state is an octal, but is split into two sections to maintain // backward compatibility. The bits are ordered as such (0-7 left to right). // +-----+-----+-+-+-+--+ -// |K0,K1|H0,H1|F|C|R|H2| +// |K0,K1|H0,H1|F|E|R|H2| // +-----+-----+-+-+-+--+ // Key state - K0,K1 is found in the 1st and 2nd bits // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits // Faceshift - F is found in the 5th bit -// Chat Circling - C is found in the 6th bit +// Eye tracker - E is found in the 6th bit // Referential Data - R is found in the 7th bit const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits const int IS_FACESHIFT_CONNECTED = 4; // 5th bit -const int UNUSED_AVATAR_STATE_BIT_5 = 5; // 6th bit (was CHAT_CIRCLING) +const int IS_EYE_TRACKER_CONNECTED = 5; // 6th bit (was CHAT_CIRCLING) const int HAS_REFERENTIAL = 6; // 7th bit const int HAND_STATE_FINGER_POINTING_BIT = 7; // 8th bit diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 7789385547..e853a3c57e 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -32,6 +32,7 @@ HeadData::HeadData(AvatarData* owningAvatar) : _lookAtPosition(0.0f, 0.0f, 0.0f), _audioLoudness(0.0f), _isFaceTrackerConnected(false), + _isEyeTrackerConnected(false), _leftEyeBlink(0.0f), _rightEyeBlink(0.0f), _averageLoudness(0.0f), diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index b180541914..3e790ff573 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -93,6 +93,7 @@ protected: glm::vec3 _lookAtPosition; float _audioLoudness; bool _isFaceTrackerConnected; + bool _isEyeTrackerConnected; float _leftEyeBlink; float _rightEyeBlink; float _averageLoudness; From 212327dd461096aba09dfed5ba23c83411f17ce7 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 29 Jul 2015 16:20:45 -0700 Subject: [PATCH 10/41] Fix eye tracker conditional compile --- interface/src/devices/EyeTracker.cpp | 6 ++++-- interface/src/devices/EyeTracker.h | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index a384202255..4cb205d158 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -34,12 +34,12 @@ EyeTracker::~EyeTracker() { #endif } +#ifdef HAVE_IVIEWHMD void EyeTracker::processData(smi_CallbackDataStruct* data) { if (!_isEnabled) { return; } -#ifdef HAVE_IVIEWHMD if (data->type == SMI_SIMPLE_GAZE_SAMPLE) { // Calculate the intersections of the left and right eye look-at vectors with a vertical plane along the monocular // gaze direction. Average these positions to give the look-at point. @@ -91,8 +91,8 @@ void EyeTracker::processData(smi_CallbackDataStruct* data) { } } } -#endif } +#endif void EyeTracker::init() { if (_isInitialized) { @@ -143,6 +143,7 @@ void EyeTracker::reset() { // Nothing to do. } +#ifdef HAVE_IVIEWHMD QString EyeTracker::smiReturnValueToString(int value) { switch (value) { @@ -168,3 +169,4 @@ QString EyeTracker::smiReturnValueToString(int value) { return number; } } +#endif diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index c41d529e91..b0b848a6de 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -17,7 +17,9 @@ #include #include +#ifdef HAVE_IVIEWHMD #include +#endif class EyeTracker : public QObject, public Dependency { @@ -32,7 +34,9 @@ public: glm::vec3 getLookAtPosition() { return _lookAtPosition; } // From mid eye point in head frame. +#ifdef HAVE_IVIEWHMD void processData(smi_CallbackDataStruct* data); +#endif public slots: void init(); From e300eb27c02cbe043fab597199068bbaedab20bb Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 3 Aug 2015 09:55:14 -0700 Subject: [PATCH 11/41] Fix libs directory path --- cmake/modules/FindiViewHMD.cmake | 4 ++-- interface/external/iViewHMD/readme.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/FindiViewHMD.cmake b/cmake/modules/FindiViewHMD.cmake index eb107a2833..c06846b218 100644 --- a/cmake/modules/FindiViewHMD.cmake +++ b/cmake/modules/FindiViewHMD.cmake @@ -3,7 +3,7 @@ # # Try to find the SMI iViewHMD eye tracker library # -# You must provide a IVIEWHMD_ROOT_DIR which contains lib and include directories +# You must provide a IVIEWHMD_ROOT_DIR which contains 3rdParty, include, and libs directories # # Once done this will define # @@ -19,7 +19,7 @@ include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("iViewHMD") find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) -find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES lib HINTS ${IVIEWHMD_SEARCH_DIRS}) +find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(iViewHMD DEFAULT_MSG IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES) diff --git a/interface/external/iViewHMD/readme.txt b/interface/external/iViewHMD/readme.txt index 7e0af7e23e..4b3d59349b 100644 --- a/interface/external/iViewHMD/readme.txt +++ b/interface/external/iViewHMD/readme.txt @@ -4,7 +4,7 @@ David Rowe, 27 Jul 2015. 1. Download and install the SMI HMD Eye Tracking software from http://update.smivision.com/iViewNG-HMD.exe. -2. Copy the SDK folders (3rdParty, include, lib) from the SDK installation folder C:\Program Files (x86)\SMI\iViewNG-HMD\SDK +2. Copy the SDK folders (3rdParty, include, libs) from the SDK installation folder C:\Program Files (x86)\SMI\iViewNG-HMD\SDK into the interface/externals/iViewHMD folder. This readme.txt should be there as well. You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different From 86ad490bdef2897f3038becaa1a2b5435c87b7a1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 3 Aug 2015 20:52:11 -0700 Subject: [PATCH 12/41] Fix to build on build PC without iViewHMD software installed --- cmake/modules/FindiViewHMD.cmake | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/cmake/modules/FindiViewHMD.cmake b/cmake/modules/FindiViewHMD.cmake index c06846b218..f4b7f832d0 100644 --- a/cmake/modules/FindiViewHMD.cmake +++ b/cmake/modules/FindiViewHMD.cmake @@ -15,13 +15,29 @@ # Copyright 2015 High Fidelity, Inc. # -include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") -hifi_library_search_hints("iViewHMD") +if (WIN32) -find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) -find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) + include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") + hifi_library_search_hints("iViewHMD") -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(iViewHMD DEFAULT_MSG IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES) + find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) -mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) + find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_path(IVIEWHMD_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) + + find_path(LIBIVIEWNG_LIBCORE_DLL_PATH libiViewNG-LibCore.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_path(OPENCV_HIGHGUI220_DLL_PATH opencv_highgui220.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_path(OPENCV_CORE200_DLL_PATH opencv_core220.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_path(OPENCV_IMGPROC220_DLL_PATH opencv_imgproc220.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) + + list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_DLL_PATH) + list(APPEND IVIEWHMD_REQUIREMENTS LIBIVIEWNG_LIBCORE_DLL_PATH OPENCV_HIGHGUI220_DLL_PATH OPENCV_CORE200_DLL_PATH OPENCV_IMGPROC220_DLL_PATH) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS}) + + add_paths_to_fixup_libs(${IVIEWHMD_DLL_PATH} ${LIBIVIEWNG_LIBCORE_DLL_PATH} ${OPENCV_HIGHGUI220_DLL_PATH} ${OPENCV_CORE200_DLL_PATH} ${OPENCV_IMGPROC220_DLL_PATH}) + + mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) + +endif () From 12010737093608f178d931c3388241941ed8958e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 13:15:26 -0700 Subject: [PATCH 13/41] Add DLLs needed for Interface to run without iViewHMD installed --- cmake/modules/FindiViewHMD.cmake | 45 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/cmake/modules/FindiViewHMD.cmake b/cmake/modules/FindiViewHMD.cmake index f4b7f832d0..f7b13f8124 100644 --- a/cmake/modules/FindiViewHMD.cmake +++ b/cmake/modules/FindiViewHMD.cmake @@ -21,23 +21,46 @@ if (WIN32) hifi_library_search_hints("iViewHMD") find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_path(IVIEWHMD_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) + find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) + list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH) - find_path(LIBIVIEWNG_LIBCORE_DLL_PATH libiViewNG-LibCore.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_path(OPENCV_HIGHGUI220_DLL_PATH opencv_highgui220.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_path(OPENCV_CORE200_DLL_PATH opencv_core220.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) - find_path(OPENCV_IMGPROC220_DLL_PATH opencv_imgproc220.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) - - list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_DLL_PATH) - list(APPEND IVIEWHMD_REQUIREMENTS LIBIVIEWNG_LIBCORE_DLL_PATH OPENCV_HIGHGUI220_DLL_PATH OPENCV_CORE200_DLL_PATH OPENCV_IMGPROC220_DLL_PATH) + set(IVIEWHMD_DLLS + avcodec-53.dll + avformat-53.dll + avutil-51.dll + libboost_filesystem-mgw45-mt-1_49.dll + libboost_system-mgw45-mt-1_49.dll + libboost_thread-mgw45-mt-1_49.dll + libgcc_s_dw2-1.dll + libiViewNG-LibCore.dll + libopencv_calib3d244.dll + libopencv_core244.dll + libopencv_features2d244.dll + libopencv_flann244.dll + libopencv_highgui244.dll + libopencv_imgproc244.dll + libopencv_legacy244.dll + libopencv_ml244.dll + libopencv_video244.dll + libstdc++-6.dll + opencv_core220.dll + opencv_highgui220.dll + opencv_imgproc220.dll + swscale-2.dll + ) + + foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS}) + find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) + list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH) + list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH}) + endforeach() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS}) - add_paths_to_fixup_libs(${IVIEWHMD_DLL_PATH} ${LIBIVIEWNG_LIBCORE_DLL_PATH} ${OPENCV_HIGHGUI220_DLL_PATH} ${OPENCV_CORE200_DLL_PATH} ${OPENCV_IMGPROC220_DLL_PATH}) + add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS}) mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) -endif () +endif() From fefddb631fb50cfe7fbd6a2a2a1af93974f0912b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 14:46:03 -0700 Subject: [PATCH 14/41] Add 1 and 3 point eye tracker calibration --- interface/src/Application.cpp | 24 +++++++++++++++++--- interface/src/Application.h | 2 ++ interface/src/Menu.cpp | 4 ++++ interface/src/Menu.h | 2 ++ interface/src/devices/EyeTracker.cpp | 33 ++++++++++++++++++++++++++++ interface/src/devices/EyeTracker.h | 2 ++ 6 files changed, 64 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f7381396a0..a0429090ba 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2034,12 +2034,30 @@ void Application::setActiveFaceTracker() { void Application::setActiveEyeTracker() { #ifdef HAVE_IVIEWHMD - bool isEyeTrackingOptionChecked = Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking); + bool isEyeTracking = Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking); + bool isSimulating = Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking); auto eyeTracker = DependencyManager::get(); - eyeTracker->setEnabled(isEyeTrackingOptionChecked, Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking)); - if (isEyeTrackingOptionChecked && !eyeTracker->isTracking()) { + eyeTracker->setEnabled(isEyeTracking, isSimulating); + if (isEyeTracking && !eyeTracker->isTracking()) { Menu::getInstance()->setIsOptionChecked(MenuOption::SMIEyeTracking, false); + isEyeTracking = false; } + Menu::getInstance()->getActionForOption(MenuOption::Calibrate1Point)->setEnabled(isEyeTracking && !isSimulating); + Menu::getInstance()->getActionForOption(MenuOption::Calibrate3Points)->setEnabled(isEyeTracking && !isSimulating); +#endif +} + +void Application::calibrateEyeTracker1Point() { +#ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + eyeTracker->calibrate(1); +#endif +} + +void Application::calibrateEyeTracker3Points() { +#ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + eyeTracker->calibrate(3); #endif } diff --git a/interface/src/Application.h b/interface/src/Application.h index 877e4c4a0d..d8ebc65db7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -409,6 +409,8 @@ public slots: void setActiveFaceTracker(); void setActiveEyeTracker(); + void calibrateEyeTracker1Point(); + void calibrateEyeTracker3Points(); void aboutApp(); void showEditEntitiesHelp(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 51d07d538d..34c4c9aada 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -440,6 +440,10 @@ Menu::Menu() { MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking"); addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false, qApp, SLOT(setActiveEyeTracker())); + addActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::Calibrate1Point, 0, + qApp, SLOT(calibrateEyeTracker1Point())); + addActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::Calibrate3Points, 0, + qApp, SLOT(calibrateEyeTracker3Points())); addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SimulateEyeTracking, 0, false, qApp, SLOT(setActiveEyeTracker())); #endif diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b3531e4140..33944674c0 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -156,6 +156,8 @@ namespace MenuOption { const QString Bookmarks = "Bookmarks"; const QString CascadedShadows = "Cascaded"; const QString CachesSize = "RAM Caches Size"; + const QString Calibrate1Point = "Calibrate - 1 Point..."; + const QString Calibrate3Points = "Calibrate - 3 Points..."; const QString CalibrateCamera = "Calibrate Camera"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 4cb205d158..39308aedfc 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -143,6 +143,39 @@ void EyeTracker::reset() { // Nothing to do. } +#ifdef HAVE_IVIEWHMD +void EyeTracker::calibrate(int points) { + smi_CalibrationHMDStruct* calibrationHMDStruct; + smi_createCalibrationHMDStruct(&calibrationHMDStruct); + + smi_CalibrationTypeEnum calibrationType; + switch (points) { + case 1: + calibrationType = SMI_ONE_POINT_CALIBRATION; + qCDebug(interfaceapp) << "Eye Tracker: One point calibration"; + break; + case 3: + calibrationType = SMI_THREE_POINT_CALIBRATION; + qCDebug(interfaceapp) << "Eye Tracker: Three point calibration"; + break; + default: + qCWarning(interfaceapp) << "Eye Tracker: Invalid calibration specified"; + return; + } + + calibrationHMDStruct->type = calibrationType; + calibrationHMDStruct->backgroundColor->blue = 0.5; + calibrationHMDStruct->backgroundColor->green = 0.5; + calibrationHMDStruct->backgroundColor->red = 0.5; + calibrationHMDStruct->foregroundColor->blue = 1.0; + calibrationHMDStruct->foregroundColor->green = 1.0; + calibrationHMDStruct->foregroundColor->red = 1.0; + + smi_setupCalibration(calibrationHMDStruct); + smi_calibrate(); +} +#endif + #ifdef HAVE_IVIEWHMD QString EyeTracker::smiReturnValueToString(int value) { switch (value) diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index b0b848a6de..57950a7062 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -36,6 +36,8 @@ public: #ifdef HAVE_IVIEWHMD void processData(smi_CallbackDataStruct* data); + + void calibrate(int points); #endif public slots: From b5fbfc645f0e5f549c0fe762f3c43232bc40317c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 15:57:19 -0700 Subject: [PATCH 15/41] Tidy eye tracker interface --- interface/src/devices/EyeTracker.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 57950a7062..3e602bb12d 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -29,10 +29,14 @@ class EyeTracker : public QObject, public Dependency { public: ~EyeTracker(); - bool isTracking() { return _isEnabled; } - bool isSimulating() { return _isSimulating; } + void init(); + void setEnabled(bool enabled, bool simulate); + void reset(); - glm::vec3 getLookAtPosition() { return _lookAtPosition; } // From mid eye point in head frame. + bool isTracking() const { return _isEnabled; } + bool isSimulating() const { return _isSimulating; } + + glm::vec3 getLookAtPosition() const { return _lookAtPosition; } // From mid eye point in head frame. #ifdef HAVE_IVIEWHMD void processData(smi_CallbackDataStruct* data); @@ -40,11 +44,6 @@ public: void calibrate(int points); #endif -public slots: - void init(); - void setEnabled(bool enabled, bool simulate); - void reset(); - private: QString smiReturnValueToString(int value); From 11ec5f5ddf44f1aed9ee362c68b99391c041c5f1 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 16:46:48 -0700 Subject: [PATCH 16/41] Don't display duplicate error message box --- interface/src/devices/EyeTracker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 39308aedfc..561f8a5a13 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -132,8 +132,8 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { _isEnabled = enabled && success; _isSimulating = _isEnabled && simulate; - if (!success) { - // Display error dialog after updating _isEnabled. + if (!success && result != SMI_ERROR_HMD_NOT_SUPPORTED) { + // Display error dialog after updating _isEnabled. Except if SMI SDK has already displayed an error message. QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } #endif From 72c4ab33a1a2c9587e7d8ba6b11a7f51197b851a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 16:54:21 -0700 Subject: [PATCH 17/41] Handle eye calibration errors --- interface/src/devices/EyeTracker.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 561f8a5a13..0e24dde8ee 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -171,8 +171,20 @@ void EyeTracker::calibrate(int points) { calibrationHMDStruct->foregroundColor->green = 1.0; calibrationHMDStruct->foregroundColor->red = 1.0; - smi_setupCalibration(calibrationHMDStruct); - smi_calibrate(); + int result = smi_setupCalibration(calibrationHMDStruct); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error setting up calibration:" << smiReturnValueToString(result); + return; + } else { + result = smi_calibrate(); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error performing calibration:" << smiReturnValueToString(result); + } + } + + if (result != SMI_RET_SUCCESS) { + QMessageBox::warning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result)); + } } #endif From c77157457818db1463e6bbe824e6635885dd3c75 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 18:02:34 -0700 Subject: [PATCH 18/41] Add 5 point eye tracker calibration option --- interface/src/Application.cpp | 12 ++++++++++-- interface/src/Application.h | 1 + interface/src/Menu.cpp | 13 +++++++++---- interface/src/Menu.h | 5 +++-- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a0429090ba..bfc23334b7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2042,8 +2042,9 @@ void Application::setActiveEyeTracker() { Menu::getInstance()->setIsOptionChecked(MenuOption::SMIEyeTracking, false); isEyeTracking = false; } - Menu::getInstance()->getActionForOption(MenuOption::Calibrate1Point)->setEnabled(isEyeTracking && !isSimulating); - Menu::getInstance()->getActionForOption(MenuOption::Calibrate3Points)->setEnabled(isEyeTracking && !isSimulating); + Menu::getInstance()->getActionForOption(MenuOption::OnePointCalibration)->setEnabled(isEyeTracking && !isSimulating); + Menu::getInstance()->getActionForOption(MenuOption::ThreePointCalibration)->setEnabled(isEyeTracking && !isSimulating); + Menu::getInstance()->getActionForOption(MenuOption::FivePointCalibration)->setEnabled(isEyeTracking && !isSimulating); #endif } @@ -2061,6 +2062,13 @@ void Application::calibrateEyeTracker3Points() { #endif } +void Application::calibrateEyeTracker5Points() { +#ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + eyeTracker->calibrate(5); +#endif +} + bool Application::exportEntities(const QString& filename, const QVector& entityIDs) { QVector entities; diff --git a/interface/src/Application.h b/interface/src/Application.h index d8ebc65db7..aaedc63592 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -411,6 +411,7 @@ public slots: void setActiveEyeTracker(); void calibrateEyeTracker1Point(); void calibrateEyeTracker3Points(); + void calibrateEyeTracker5Points(); void aboutApp(); void showEditEntitiesHelp(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 34c4c9aada..4a77a342a2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -440,10 +440,15 @@ Menu::Menu() { MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking"); addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false, qApp, SLOT(setActiveEyeTracker())); - addActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::Calibrate1Point, 0, - qApp, SLOT(calibrateEyeTracker1Point())); - addActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::Calibrate3Points, 0, - qApp, SLOT(calibrateEyeTracker3Points())); + { + MenuWrapper* calibrateEyeTrackingMenu = eyeTrackingMenu->addMenu("Calibrate"); + addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::OnePointCalibration, 0, + qApp, SLOT(calibrateEyeTracker1Point())); + addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::ThreePointCalibration, 0, + qApp, SLOT(calibrateEyeTracker3Point())); + addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::FivePointCalibration, 0, + qApp, SLOT(calibrateEyeTracker5Point())); + } addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SimulateEyeTracking, 0, false, qApp, SLOT(setActiveEyeTracker())); #endif diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 33944674c0..70186f3307 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -156,8 +156,6 @@ namespace MenuOption { const QString Bookmarks = "Bookmarks"; const QString CascadedShadows = "Cascaded"; const QString CachesSize = "RAM Caches Size"; - const QString Calibrate1Point = "Calibrate - 1 Point..."; - const QString Calibrate3Points = "Calibrate - 3 Points..."; const QString CalibrateCamera = "Calibrate Camera"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; @@ -197,6 +195,7 @@ namespace MenuOption { const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; const QString FirstPerson = "First Person"; + const QString FivePointCalibration = "5 Point Calibration"; const QString Forward = "Forward"; const QString FrameTimer = "Show Timer"; const QString Fullscreen = "Fullscreen"; @@ -221,6 +220,7 @@ namespace MenuOption { const QString NamesAboveHeads = "Names Above Heads"; const QString NoFaceTracking = "None"; const QString OctreeStats = "Entity Statistics"; + const QString OnePointCalibration = "1 Point Calibration"; const QString OnlyDisplayTopTen = "Only Display Top Ten"; const QString PackageModel = "Package Model..."; const QString Pair = "Pair"; @@ -283,6 +283,7 @@ namespace MenuOption { const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; const QString TestPing = "Test Ping"; const QString ThirdPerson = "Third Person"; + const QString ThreePointCalibration = "3 Point Calibration"; const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; const QString ToolWindow = "Tool Window"; const QString TransmitterDrive = "Transmitter Drive"; From fae26d901cb3d30eba78277e13641aa9d885f53d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 4 Aug 2015 18:34:56 -0700 Subject: [PATCH 19/41] Add menu option to render look-at targets --- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/Avatar.cpp | 2 ++ interface/src/avatar/Avatar.h | 1 + interface/src/avatar/Head.cpp | 17 +++++++++++++++++ interface/src/avatar/Head.h | 3 +++ tests/ui/src/main.cpp | 1 + 7 files changed, 26 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4a77a342a2..c124f2caec 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -461,6 +461,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtTargets, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 70186f3307..ce5388449f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -234,6 +234,7 @@ namespace MenuOption { const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes"; const QString RenderFocusIndicator = "Show Eye Focus"; const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; + const QString RenderLookAtTargets = "Show Look-at Targets"; const QString RenderLookAtVectors = "Show Look-at Vectors"; const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; const QString RenderTargetFramerate = "Framerate"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e29e5e4408..abb671f207 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -69,6 +69,8 @@ namespace render { auto avatarPtr = static_pointer_cast(avatar); bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors); avatarPtr->setDisplayingLookatVectors(renderLookAtVectors); + bool renderLookAtTarget = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtTargets); + avatarPtr->setDisplayingLookatTarget(renderLookAtTarget); if (avatarPtr->isInitialized() && args) { avatarPtr->render(args, Application::getInstance()->getCamera()->getPosition()); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 6035c44389..07a5ea2b89 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -91,6 +91,7 @@ public: //setters void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); } + void setDisplayingLookatTarget(bool displayingLookatTarget) { getHead()->setRenderLookatTarget(displayingLookatTarget); } void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; } bool getIsLookAtTarget() const { return _isLookAtTarget; } //getters diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 481a8ca9e8..aa7084b8e0 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -46,6 +46,7 @@ Head::Head(Avatar* owningAvatar) : _mouth3(0.0f), _mouth4(0.0f), _renderLookatVectors(false), + _renderLookatTarget(false), _saccade(0.0f, 0.0f, 0.0f), _saccadeTarget(0.0f, 0.0f, 0.0f), _leftEyeBlinkVelocity(0.0f), @@ -305,6 +306,9 @@ void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustu if (_renderLookatVectors) { renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); } + if (_renderLookatTarget) { + renderLookatTarget(renderArgs, getCorrectedLookAtPosition()); + } } void Head::setScale (float scale) { @@ -414,4 +418,17 @@ void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID); } +void Head::renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition) { + auto& batch = *renderArgs->_batch; + auto transform = Transform{}; + transform.setTranslation(lookatPosition); + batch.setModelTransform(transform); + auto deferredLighting = DependencyManager::get(); + deferredLighting->bindSimpleProgram(batch); + + auto geometryCache = DependencyManager::get(); + const float LOOK_AT_TARGET_RADIUS = 0.03f; + const glm::vec4 LOOK_AT_TARGET_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; + geometryCache->renderSphere(batch, LOOK_AT_TARGET_RADIUS, 15, 15, LOOK_AT_TARGET_COLOR, true); +} diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index a1c70f9dff..dff4702246 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -39,6 +39,7 @@ public: void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; } void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } + void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; } /// \return orientationBase+Delta glm::quat getFinalOrientationInLocalFrame() const; @@ -123,6 +124,7 @@ private: float _mouth3; float _mouth4; bool _renderLookatVectors; + bool _renderLookatTarget; glm::vec3 _saccade; glm::vec3 _saccadeTarget; float _leftEyeBlinkVelocity; @@ -151,6 +153,7 @@ private: // private methods void renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition); + void renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition); void calculateMouthShapes(); void applyEyelidOffset(glm::quat headOrientation); diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index e8ab7e02df..2de2e1e6c6 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -177,6 +177,7 @@ public: RenderFocusIndicator, RenderHeadCollisionShapes, RenderLookAtVectors, + RenderLookAtTargets, RenderSkeletonCollisionShapes, RenderTargetFramerate, RenderTargetFramerateUnlimited, From 3be2ddf234b775178bc9d2ee2669b88e3c816d2c Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 5 Aug 2015 12:05:04 -0700 Subject: [PATCH 20/41] Fix calibration menu item actions --- interface/src/Menu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c124f2caec..f1d03d4e78 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -445,9 +445,9 @@ Menu::Menu() { addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::OnePointCalibration, 0, qApp, SLOT(calibrateEyeTracker1Point())); addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::ThreePointCalibration, 0, - qApp, SLOT(calibrateEyeTracker3Point())); + qApp, SLOT(calibrateEyeTracker3Points())); addActionToQMenuAndActionHash(calibrateEyeTrackingMenu, MenuOption::FivePointCalibration, 0, - qApp, SLOT(calibrateEyeTracker5Point())); + qApp, SLOT(calibrateEyeTracker5Points())); } addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SimulateEyeTracking, 0, false, qApp, SLOT(setActiveEyeTracker())); From a15943d941a9bc87443fc6b0677559825f068db5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 5 Aug 2015 13:39:26 -0700 Subject: [PATCH 21/41] Increase size of look-at target sphere --- interface/src/avatar/Head.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index aa7084b8e0..46f38acb33 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -428,7 +428,7 @@ void Head::renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition) deferredLighting->bindSimpleProgram(batch); auto geometryCache = DependencyManager::get(); - const float LOOK_AT_TARGET_RADIUS = 0.03f; + const float LOOK_AT_TARGET_RADIUS = 0.075f; const glm::vec4 LOOK_AT_TARGET_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; geometryCache->renderSphere(batch, LOOK_AT_TARGET_RADIUS, 15, 15, LOOK_AT_TARGET_COLOR, true); } From e8eac7db3432a56d131a645b35eb8428c38d98a5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 5 Aug 2015 13:40:57 -0700 Subject: [PATCH 22/41] Render look-at vectors and target in first person view --- interface/src/avatar/Avatar.cpp | 2 ++ interface/src/avatar/Head.cpp | 3 +++ interface/src/avatar/Head.h | 1 + interface/src/avatar/MyAvatar.cpp | 1 + 4 files changed, 7 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index abb671f207..ecae0b017e 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -603,7 +603,9 @@ void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, floa getHand()->render(renderArgs, false); } + getHead()->render(renderArgs, 1.0f, renderFrustum); + getHead()->renderLookAts(renderArgs); } bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 46f38acb33..bdd53cfd7f 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -303,6 +303,9 @@ void Head::relaxLean(float deltaTime) { } void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) { +} + +void Head::renderLookAts(RenderArgs* renderArgs) { if (_renderLookatVectors) { renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index dff4702246..43042422d5 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -40,6 +40,7 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; } + void renderLookAts(RenderArgs* renderArgs); /// \return orientationBase+Delta glm::quat getFinalOrientationInLocalFrame() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2d7cf4ca5e..b1c2d113f9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1160,6 +1160,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl if (shouldRenderHead(renderArgs)) { getHead()->render(renderArgs, 1.0f, renderFrustum); } + getHead()->renderLookAts(renderArgs); getHand()->render(renderArgs, true); } From 551c00dc23eea3722712f9863aeb03b987c76982 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 6 Aug 2015 16:41:06 -0700 Subject: [PATCH 23/41] Fix look-at position calculation when in HMD display Calculate look-at target from camera position and orientation. Render own look-at vectors from camera eye positions. Draw other avatar's look-at vectors from their eyeballs. With and without eye trackers. --- interface/src/Application.cpp | 20 +++++++++++++++----- interface/src/avatar/Head.cpp | 4 ++-- interface/src/avatar/Head.h | 2 +- interface/src/avatar/MyAvatar.cpp | 10 +++++++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c3d810fb0b..992687d114 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2347,10 +2347,15 @@ void Application::updateMyAvatarLookAtPosition() { } else { lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition(); } - } else if (eyeTracker->isTracking() && (isHMDMode() || eyeTracker->isSimulating())) { + } else if (eyeTracker->isTracking() && (OculusManager::isConnected() || eyeTracker->isSimulating())) { // Look at the point that the user is looking at. - lookAtSpot = _myAvatar->getHead()->getEyePosition() + - (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition()); + if (OculusManager::isConnected()) { + lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition() + + _myAvatar->getOrientation() * (OculusManager::getOrientation() * eyeTracker->getLookAtPosition()); + } else { + lookAtSpot = _myAvatar->getHead()->getEyePosition() + + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition()); + } } else { AvatarSharedPointer lookingAt = _myAvatar->getLookAtTargetAvatar().lock(); if (lookingAt && _myAvatar != lookingAt.get()) { @@ -2383,8 +2388,13 @@ void Application::updateMyAvatarLookAtPosition() { } } else { // I am not looking at anyone else, so just look forward - lookAtSpot = _myAvatar->getHead()->getEyePosition() + - (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + if (OculusManager::isConnected()) { + lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition() + + _myAvatar->getOrientation() * (OculusManager::getOrientation() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + } else { + lookAtSpot = _myAvatar->getHead()->getEyePosition() + + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + } } // Deflect the eyes a bit to match the detected gaze from the face tracker if active. diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index a993fddf1c..b9cdb174d3 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -305,9 +305,9 @@ void Head::relaxLean(float deltaTime) { void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) { } -void Head::renderLookAts(RenderArgs* renderArgs) { +void Head::renderLookAts(RenderArgs* renderArgs, glm::vec3 eyeOffset) { if (_renderLookatVectors) { - renderLookatVectors(renderArgs, _leftEyePosition, _rightEyePosition, getCorrectedLookAtPosition()); + renderLookatVectors(renderArgs, _leftEyePosition + eyeOffset, _rightEyePosition + eyeOffset, getCorrectedLookAtPosition()); } if (_renderLookatTarget) { renderLookatTarget(renderArgs, getCorrectedLookAtPosition()); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 43042422d5..8beaf26f36 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -40,7 +40,7 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; } - void renderLookAts(RenderArgs* renderArgs); + void renderLookAts(RenderArgs* renderArgs, glm::vec3 eyeOffset = glm::vec3()); /// \return orientationBase+Delta glm::quat getFinalOrientationInLocalFrame() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 629c873323..7c8fd0dfce 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1166,7 +1166,15 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl if (shouldRenderHead(renderArgs)) { getHead()->render(renderArgs, 1.0f, renderFrustum); } - getHead()->renderLookAts(renderArgs); + + if (qApp->isHMDMode()) { + glm::vec3 eyeOffset = + OculusManager::getMidEyePosition() + Application::getInstance()->getCamera()->getPosition() - getEyePosition(); + getHead()->renderLookAts(renderArgs, eyeOffset); + } else { + getHead()->renderLookAts(renderArgs); + } + getHand()->render(renderArgs, true); } From 427b8b973c9ea5d8a8c77131a427302e82b06bec Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 6 Aug 2015 17:20:03 -0700 Subject: [PATCH 24/41] Fix excluding saccades when doing eye tracking --- interface/src/avatar/Head.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b9cdb174d3..d6aedc5e20 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -131,20 +131,25 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { setTorsoTwist(currentTwist + (getFinalYaw() * BODY_FOLLOW_HEAD_FACTOR - currentTwist) * BODY_FOLLOW_HEAD_YAW_RATE); } - if (!(_isFaceTrackerConnected || _isEyeTrackerConnected || billboard)) { - // Update eye saccades - const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; - const float AVERAGE_SACCADE_INTERVAL = 6.0f; - const float MICROSACCADE_MAGNITUDE = 0.002f; - const float SACCADE_MAGNITUDE = 0.04f; - const float NOMINAL_FRAME_RATE = 60.0f; + if (!(_isFaceTrackerConnected || billboard)) { - if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { - _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); - } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { - _saccadeTarget = SACCADE_MAGNITUDE * randVector(); + if (!_isEyeTrackerConnected) { + // Update eye saccades + const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; + const float AVERAGE_SACCADE_INTERVAL = 6.0f; + const float MICROSACCADE_MAGNITUDE = 0.002f; + const float SACCADE_MAGNITUDE = 0.04f; + const float NOMINAL_FRAME_RATE = 60.0f; + + if (randFloat() < deltaTime / AVERAGE_MICROSACCADE_INTERVAL) { + _saccadeTarget = MICROSACCADE_MAGNITUDE * randVector(); + } else if (randFloat() < deltaTime / AVERAGE_SACCADE_INTERVAL) { + _saccadeTarget = SACCADE_MAGNITUDE * randVector(); + } + _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); + } else { + _saccade = glm::vec3(); } - _saccade += (_saccadeTarget - _saccade) * pow(0.5f, NOMINAL_FRAME_RATE * deltaTime); // Detect transition from talking to not; force blink after that and a delay bool forceBlink = false; From 738c5658fa403b64d642325696b2f0aac3efff26 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 6 Aug 2015 20:46:51 -0700 Subject: [PATCH 25/41] Guard against NaNs in eye tracker look-at position --- interface/src/devices/EyeTracker.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 0e24dde8ee..c8942a889b 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -76,20 +76,27 @@ void EyeTracker::processData(smi_CallbackDataStruct* data) { float rightLinePlaneDotProduct = glm::dot(rightLineDirection, planeNormal); // Gaze into distance if eyes are parallel or diverged; otherwise the look-at is the average of look-at points + glm::vec3 lookAtPosition; if (abs(leftLinePlaneDotProduct) <= FLT_EPSILON || abs(rightLinePlaneDotProduct) <= FLT_EPSILON) { - _lookAtPosition = monocularDirection * (float)TREE_SCALE; + lookAtPosition = monocularDirection * (float)TREE_SCALE; } else { float leftDistance = glm::dot(planePoint - leftLinePoint, planeNormal) / leftLinePlaneDotProduct; float rightDistance = glm::dot(planePoint - rightLinePoint, planeNormal) / rightLinePlaneDotProduct; if (leftDistance <= 0.0f || rightDistance <= 0.0f || leftDistance > (float)TREE_SCALE || rightDistance > (float)TREE_SCALE) { - _lookAtPosition = monocularDirection * (float)TREE_SCALE; + lookAtPosition = monocularDirection * (float)TREE_SCALE; } else { glm::vec3 leftIntersectionPoint = leftLinePoint + leftDistance * leftLineDirection; glm::vec3 rightIntersectionPoint = rightLinePoint + rightDistance * rightLineDirection; - _lookAtPosition = (leftIntersectionPoint + rightIntersectionPoint) / 2.0f; + lookAtPosition = (leftIntersectionPoint + rightIntersectionPoint) / 2.0f; } } + + if (glm::isnan(lookAtPosition.x) || glm::isnan(lookAtPosition.y) || glm::isnan(lookAtPosition.z)) { + return; + } + + _lookAtPosition = lookAtPosition; } } #endif From e37fd956eda288d464ab827ba40c7fbb8153a25b Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 6 Aug 2015 22:51:25 -0700 Subject: [PATCH 26/41] Fix look-at target position getting stuck when avatar moves out of range --- interface/src/avatar/MyAvatar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7c8fd0dfce..848838a4e5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -859,6 +859,8 @@ void MyAvatar::updateLookAtTargetAvatar() { } else { avatar->getHead()->clearCorrectedLookAtPosition(); } + } else { + avatar->getHead()->clearCorrectedLookAtPosition(); } } auto avatarPointer = _lookAtTargetAvatar.lock(); From e2bf5cad3e474104b6a7e92a55f4318314659bce Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Aug 2015 11:59:11 -0700 Subject: [PATCH 27/41] Resume eye tracking at start-up if was running at shut-down --- interface/src/Application.cpp | 6 +++++- interface/src/devices/EyeTracker.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 992687d114..c9047e527f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2042,9 +2042,13 @@ void Application::setActiveFaceTracker() { void Application::setActiveEyeTracker() { #ifdef HAVE_IVIEWHMD + auto eyeTracker = DependencyManager::get(); + if (!eyeTracker->isInitialized()) { + return; + } + bool isEyeTracking = Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking); bool isSimulating = Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking); - auto eyeTracker = DependencyManager::get(); eyeTracker->setEnabled(isEyeTracking, isSimulating); if (isEyeTracking && !eyeTracker->isTracking()) { Menu::getInstance()->setIsOptionChecked(MenuOption::SMIEyeTracking, false); diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 3e602bb12d..8747076340 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -33,6 +33,7 @@ public: void setEnabled(bool enabled, bool simulate); void reset(); + bool isInitialized() const { return _isInitialized; } bool isTracking() const { return _isEnabled; } bool isSimulating() const { return _isSimulating; } From ea03067a02515ab967ff6db502caae0b96f4e461 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Aug 2015 12:23:43 -0700 Subject: [PATCH 28/41] When eye tracking data is lot for > N seconds handle as "not tracking" Revert to default look-at behaviour and saccades until tracking resumes. --- interface/src/Application.cpp | 2 +- interface/src/devices/EyeTracker.cpp | 9 +++++++++ interface/src/devices/EyeTracker.h | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c9047e527f..56a33695bc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2050,7 +2050,7 @@ void Application::setActiveEyeTracker() { bool isEyeTracking = Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking); bool isSimulating = Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking); eyeTracker->setEnabled(isEyeTracking, isSimulating); - if (isEyeTracking && !eyeTracker->isTracking()) { + if (isEyeTracking && !eyeTracker->isEnabled()) { Menu::getInstance()->setIsOptionChecked(MenuOption::SMIEyeTracking, false); isEyeTracking = false; } diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index c8942a889b..9a35448bd0 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -13,6 +13,8 @@ #include +#include + #include "InterfaceLogging.h" #include "OctreeConstants.h" @@ -36,6 +38,8 @@ EyeTracker::~EyeTracker() { #ifdef HAVE_IVIEWHMD void EyeTracker::processData(smi_CallbackDataStruct* data) { + _lastProcessDataTimestamp = usecTimestampNow(); + if (!_isEnabled) { return; } @@ -150,6 +154,11 @@ void EyeTracker::reset() { // Nothing to do. } +bool EyeTracker::isTracking() const { + static const quint64 ACTIVE_TIMEOUT_USECS = 2000000; // 2 secs + return (usecTimestampNow() - _lastProcessDataTimestamp < ACTIVE_TIMEOUT_USECS); +} + #ifdef HAVE_IVIEWHMD void EyeTracker::calibrate(int points) { smi_CalibrationHMDStruct* calibrationHMDStruct; diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 8747076340..4976c14d08 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -34,7 +34,8 @@ public: void reset(); bool isInitialized() const { return _isInitialized; } - bool isTracking() const { return _isEnabled; } + bool isEnabled() const { return _isEnabled; } + bool isTracking() const; bool isSimulating() const { return _isSimulating; } glm::vec3 getLookAtPosition() const { return _lookAtPosition; } // From mid eye point in head frame. @@ -52,6 +53,8 @@ private: bool _isEnabled = false; bool _isSimulating = false; + quint64 _lastProcessDataTimestamp; + glm::vec3 _lookAtPosition; }; From b54c2526c19b28009860de608957b502f6ea0d11 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 7 Aug 2015 16:13:25 -0700 Subject: [PATCH 29/41] Fix rendering own look-at vectors in HMD mode Vectors now originate at user's physical eyeballs rather than at user avatar's eye separation. --- interface/src/avatar/Head.cpp | 8 ++++++-- interface/src/avatar/Head.h | 3 ++- interface/src/avatar/MyAvatar.cpp | 6 +++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index d6aedc5e20..0ae395f61a 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -310,9 +310,13 @@ void Head::relaxLean(float deltaTime) { void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) { } -void Head::renderLookAts(RenderArgs* renderArgs, glm::vec3 eyeOffset) { +void Head::renderLookAts(RenderArgs* renderArgs) { + renderLookAts(renderArgs, _leftEyePosition, _rightEyePosition); +} + +void Head::renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition) { if (_renderLookatVectors) { - renderLookatVectors(renderArgs, _leftEyePosition + eyeOffset, _rightEyePosition + eyeOffset, getCorrectedLookAtPosition()); + renderLookatVectors(renderArgs, leftEyePosition, rightEyePosition, getCorrectedLookAtPosition()); } if (_renderLookatTarget) { renderLookatTarget(renderArgs, getCorrectedLookAtPosition()); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 8beaf26f36..691775b029 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -40,7 +40,8 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; } - void renderLookAts(RenderArgs* renderArgs, glm::vec3 eyeOffset = glm::vec3()); + void renderLookAts(RenderArgs* renderArgs); + void renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition); /// \return orientationBase+Delta glm::quat getFinalOrientationInLocalFrame() const; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3578acaa60..59eb899f54 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1167,9 +1167,9 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } if (qApp->isHMDMode()) { - glm::vec3 eyeOffset = - OculusManager::getMidEyePosition() + Application::getInstance()->getCamera()->getPosition() - getEyePosition(); - getHead()->renderLookAts(renderArgs, eyeOffset); + glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); + getHead()->renderLookAts(renderArgs, cameraPosition + OculusManager::getLeftEyePosition(), + cameraPosition + OculusManager::getRightEyePosition()); } else { getHead()->renderLookAts(renderArgs); } From 4f3c4a531227fec3ea77e5ee6978e676240f53e0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 10 Aug 2015 12:52:47 -0700 Subject: [PATCH 30/41] Tidy up handling of eye tracker streaming --- interface/src/devices/EyeTracker.cpp | 14 +++++++++----- interface/src/devices/EyeTracker.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 9a35448bd0..7620dc070f 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -29,9 +29,11 @@ static void CALLBACK eyeTrackerCallback(smi_CallbackDataStruct* data) { EyeTracker::~EyeTracker() { #ifdef HAVE_IVIEWHMD - int result = smi_quit(); - if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error terminating tracking:" << smiReturnValueToString(result); + if (_isStreaming) { + int result = smi_quit(); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error terminating tracking:" << smiReturnValueToString(result); + } } #endif } @@ -131,16 +133,18 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { qCDebug(interfaceapp) << "Eye Tracker: Set enabled =" << enabled << ", simulate =" << simulate; bool success = true; int result = 0; - if (enabled) { + if (enabled && !_isStreaming) { // There is no smi_stopStreaming() method so keep streaming once started in case tracking is re-enabled after stopping. result = smi_startStreaming(simulate); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); success = false; } + + _isStreaming = success; } - _isEnabled = enabled && success; + _isEnabled = enabled && _isStreaming; _isSimulating = _isEnabled && simulate; if (!success && result != SMI_ERROR_HMD_NOT_SUPPORTED) { diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index 4976c14d08..b1c71c0303 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -50,6 +50,7 @@ private: QString smiReturnValueToString(int value); bool _isInitialized = false; + bool _isStreaming = false; bool _isEnabled = false; bool _isSimulating = false; From 658434e02091e2674ca1b2278e48a5af10f703b2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 10 Aug 2015 13:20:42 -0700 Subject: [PATCH 31/41] Automatically save and restore eye tracker calibration Automatically saves and restores just a single calibration so that user doesn't have to manually save or load, which isn't very convenient to do with an HMD on. --- interface/src/devices/EyeTracker.cpp | 42 +++++++++++++++++++++------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 7620dc070f..5f89ca0732 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -18,6 +18,10 @@ #include "InterfaceLogging.h" #include "OctreeConstants.h" +#ifdef HAVE_IVIEWHMD +char* HIGH_FIDELITY_EYE_TRACKER_CALIBRATION = "HighFidelityEyeTrackerCalibration"; +#endif + #ifdef HAVE_IVIEWHMD static void CALLBACK eyeTrackerCallback(smi_CallbackDataStruct* data) { auto eyeTracker = DependencyManager::get(); @@ -131,26 +135,35 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { #ifdef HAVE_IVIEWHMD qCDebug(interfaceapp) << "Eye Tracker: Set enabled =" << enabled << ", simulate =" << simulate; - bool success = true; - int result = 0; if (enabled && !_isStreaming) { // There is no smi_stopStreaming() method so keep streaming once started in case tracking is re-enabled after stopping. - result = smi_startStreaming(simulate); + int result = smi_startStreaming(simulate); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); - success = false; + // Display error dialog except if SMI SDK has already displayed an error message. + if (result != SMI_ERROR_HMD_NOT_SUPPORTED) { + QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + } } - _isStreaming = success; + _isStreaming = (result == SMI_RET_SUCCESS); + + if (_isStreaming) { + // Automatically load calibration if one has been saved. + QString availableCalibrations = QString(smi_getAvailableCalibrations()); + if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { + result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); + QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + + smiReturnValueToString(result)); + } + } + } } _isEnabled = enabled && _isStreaming; _isSimulating = _isEnabled && simulate; - - if (!success && result != SMI_ERROR_HMD_NOT_SUPPORTED) { - // Display error dialog after updating _isEnabled. Except if SMI SDK has already displayed an error message. - QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); - } #endif } @@ -199,6 +212,11 @@ void EyeTracker::calibrate(int points) { result = smi_calibrate(); if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error performing calibration:" << smiReturnValueToString(result); + } else { + result = smi_saveCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error saving calibration:" << smiReturnValueToString(result); + } } } @@ -226,6 +244,10 @@ QString EyeTracker::smiReturnValueToString(int value) { return "Eye cameras not available"; case smi_ErrorReturnValue::SMI_ERROR_OCULUS_RUNTIME_NOT_SUPPORTED: return "Oculus runtime not supported"; + case smi_ErrorReturnValue::SMI_ERROR_FILE_NOT_FOUND: + return "File not found"; + case smi_ErrorReturnValue::SMI_ERROR_FILE_EMPTY: + return "File empty"; case smi_ErrorReturnValue::SMI_ERROR_UNKNOWN: return "Unknown error"; default: From e7beb544f6b03421daa6c9ccb9b8b7ff192a3651 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 10 Aug 2015 13:22:46 -0700 Subject: [PATCH 32/41] Log successfully loading eye tracker calibration --- interface/src/devices/EyeTracker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 5f89ca0732..ee688b1f38 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -157,6 +157,8 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + smiReturnValueToString(result)); + } else { + qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; } } } From 81d8edae5f24b2fd60aa68b19cd79d0c3e2a7654 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 10 Aug 2015 13:23:14 -0700 Subject: [PATCH 33/41] Add missing 5 point calibration --- interface/src/devices/EyeTracker.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index ee688b1f38..41ca6edccc 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -193,6 +193,10 @@ void EyeTracker::calibrate(int points) { calibrationType = SMI_THREE_POINT_CALIBRATION; qCDebug(interfaceapp) << "Eye Tracker: Three point calibration"; break; + case 5: + calibrationType = SMI_FIVE_POINT_CALIBRATION; + qCDebug(interfaceapp) << "Eye Tracker: Five point calibration"; + break; default: qCWarning(interfaceapp) << "Eye Tracker: Invalid calibration specified"; return; From 8b326414accae284879c1eaa13f12f36a99034aa Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 10 Aug 2015 19:19:29 -0700 Subject: [PATCH 34/41] Start eye tracker streaming such that don't block Interface Start method is called in a separate thread. --- interface/src/Application.cpp | 5 +- interface/src/devices/EyeTracker.cpp | 94 +++++++++++++++++++--------- interface/src/devices/EyeTracker.h | 11 +++- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2fd18a4fe2..cd919da2cc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2050,10 +2050,7 @@ void Application::setActiveEyeTracker() { bool isEyeTracking = Menu::getInstance()->isOptionChecked(MenuOption::SMIEyeTracking); bool isSimulating = Menu::getInstance()->isOptionChecked(MenuOption::SimulateEyeTracking); eyeTracker->setEnabled(isEyeTracking, isSimulating); - if (isEyeTracking && !eyeTracker->isEnabled()) { - Menu::getInstance()->setIsOptionChecked(MenuOption::SMIEyeTracking, false); - isEyeTracking = false; - } + Menu::getInstance()->getActionForOption(MenuOption::OnePointCalibration)->setEnabled(isEyeTracking && !isSimulating); Menu::getInstance()->getActionForOption(MenuOption::ThreePointCalibration)->setEnabled(isEyeTracking && !isSimulating); Menu::getInstance()->getActionForOption(MenuOption::FivePointCalibration)->setEnabled(isEyeTracking && !isSimulating); diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 41ca6edccc..b513bd7ac7 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -11,7 +11,9 @@ #include "EyeTracker.h" +#include #include +#include #include @@ -125,9 +127,47 @@ void EyeTracker::init() { } else { _isInitialized = true; } + + connect(&_startStreamingWatcher, SIGNAL(finished()), this, SLOT(onStreamStarted())); #endif } +#ifdef HAVE_IVIEWHMD +int EyeTracker::startStreaming(bool simulate) { + return smi_startStreaming(simulate); // This call blocks execution. +} +#endif + +#ifdef HAVE_IVIEWHMD +void EyeTracker::onStreamStarted() { + int result = _startStreamingWatcher.result(); + _isStreaming = (result == SMI_RET_SUCCESS); + + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); + // Display error dialog unless SMI SDK has already displayed an error message. + if (result != SMI_ERROR_HMD_NOT_SUPPORTED) { + QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); + } + } + + if (_isStreaming) { + // Automatically load calibration if one has been saved. + QString availableCalibrations = QString(smi_getAvailableCalibrations()); + if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { + result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + if (result != SMI_RET_SUCCESS) { + qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); + QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + + smiReturnValueToString(result)); + } else { + qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; + } + } + } +} +#endif + void EyeTracker::setEnabled(bool enabled, bool simulate) { if (!_isInitialized) { return; @@ -135,37 +175,29 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) { #ifdef HAVE_IVIEWHMD qCDebug(interfaceapp) << "Eye Tracker: Set enabled =" << enabled << ", simulate =" << simulate; - if (enabled && !_isStreaming) { - // There is no smi_stopStreaming() method so keep streaming once started in case tracking is re-enabled after stopping. - int result = smi_startStreaming(simulate); + + // There is no smi_stopStreaming() method and after an smi_quit(), streaming cannot be restarted (at least not for + // simulated data). So keep streaming once started in case tracking is re-enabled after stopping. + + // Try to stop streaming if changing whether simulating or not. + if (enabled && _isStreaming && _isStreamSimulating != simulate) { + int result = smi_quit(); if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result); - // Display error dialog except if SMI SDK has already displayed an error message. - if (result != SMI_ERROR_HMD_NOT_SUPPORTED) { - QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); - } - } - - _isStreaming = (result == SMI_RET_SUCCESS); - - if (_isStreaming) { - // Automatically load calibration if one has been saved. - QString availableCalibrations = QString(smi_getAvailableCalibrations()); - if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { - result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); - if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); - QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" - + smiReturnValueToString(result)); - } else { - qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; - } - } + qCWarning(interfaceapp) << "Eye Tracker: Error stopping streaming:" << smiReturnValueToString(result); } + _isStreaming = false; } - _isEnabled = enabled && _isStreaming; - _isSimulating = _isEnabled && simulate; + if (enabled && !_isStreaming) { + // Start SMI streaming in a separate thread because it blocks. + QFuture future = QtConcurrent::run(this, &EyeTracker::startStreaming, simulate); + _startStreamingWatcher.setFuture(future); + _isStreamSimulating = simulate; + } + + _isEnabled = enabled; + _isSimulating = simulate; + #endif } @@ -175,11 +207,17 @@ void EyeTracker::reset() { bool EyeTracker::isTracking() const { static const quint64 ACTIVE_TIMEOUT_USECS = 2000000; // 2 secs - return (usecTimestampNow() - _lastProcessDataTimestamp < ACTIVE_TIMEOUT_USECS); + return _isEnabled && (usecTimestampNow() - _lastProcessDataTimestamp < ACTIVE_TIMEOUT_USECS); } #ifdef HAVE_IVIEWHMD void EyeTracker::calibrate(int points) { + + if (!_isStreaming) { + qCWarning(interfaceapp) << "Eye Tracker: Cannot calibrate because not streaming"; + return; + } + smi_CalibrationHMDStruct* calibrationHMDStruct; smi_createCalibrationHMDStruct(&calibrationHMDStruct); diff --git a/interface/src/devices/EyeTracker.h b/interface/src/devices/EyeTracker.h index b1c71c0303..0e760d9454 100644 --- a/interface/src/devices/EyeTracker.h +++ b/interface/src/devices/EyeTracker.h @@ -13,6 +13,7 @@ #define hifi_EyeTracker_h #include +#include #include @@ -44,19 +45,27 @@ public: void processData(smi_CallbackDataStruct* data); void calibrate(int points); + + int startStreaming(bool simulate); + +private slots: + void onStreamStarted(); #endif private: QString smiReturnValueToString(int value); bool _isInitialized = false; - bool _isStreaming = false; bool _isEnabled = false; bool _isSimulating = false; + bool _isStreaming = false; + bool _isStreamSimulating = false; quint64 _lastProcessDataTimestamp; glm::vec3 _lookAtPosition; + + QFutureWatcher _startStreamingWatcher; }; #endif // hifi_EyeTracker_h From f7dc5480b736e5e25abbddb7fde2d585c868ba54 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 10 Aug 2015 20:20:51 -0700 Subject: [PATCH 35/41] Fix #include path for OSX/Linux --- interface/src/devices/EyeTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index b513bd7ac7..bea04c16a2 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include From 3e8d0ad712ad3e0993bc90b353c1931bf9142e59 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 11 Aug 2015 13:40:08 -0700 Subject: [PATCH 36/41] Fix to use new HMD methods --- interface/src/Application.cpp | 30 +++++++++++++++++++++++------- interface/src/Application.h | 3 ++- interface/src/avatar/MyAvatar.cpp | 14 ++++++++++++-- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fa85aa2192..23fc1f13e1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2371,11 +2371,19 @@ void Application::updateMyAvatarLookAtPosition() { } else { lookAtSpot = _myCamera.getPosition() + transformPoint(_myAvatar->getSensorToWorldMatrix(), extractTranslation(getHMDSensorPose())); } - } else if (eyeTracker->isTracking() && (OculusManager::isConnected() || eyeTracker->isSimulating())) { + } else if (eyeTracker->isTracking() && (isHMDMode() || eyeTracker->isSimulating())) { // Look at the point that the user is looking at. - if (OculusManager::isConnected()) { - lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition() + - _myAvatar->getOrientation() * (OculusManager::getOrientation() * eyeTracker->getLookAtPosition()); + if (isHMDMode()) { + + // TODO: Test ... + // Position of simulated look-at target should change with change in HMD position and orientation + + glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); + glm::quat hmdOrientation = glm::quat_cast(headPose); + glm::vec3 hmdPosition = glm::vec3(headPose[3]); + + lookAtSpot = _myCamera.getPosition() + hmdPosition + + _myAvatar->getOrientation() * (hmdOrientation * eyeTracker->getLookAtPosition()); } else { lookAtSpot = _myAvatar->getHead()->getEyePosition() + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition()); @@ -2412,9 +2420,17 @@ void Application::updateMyAvatarLookAtPosition() { } } else { // I am not looking at anyone else, so just look forward - if (OculusManager::isConnected()) { - lookAtSpot = _myCamera.getPosition() + OculusManager::getMidEyePosition() + - _myAvatar->getOrientation() * (OculusManager::getOrientation() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + if (isHMDMode()) { + + // TODO: Test ... + // Look-at vector should go into distance based on HMD position and orientation + + glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); + glm::quat hmdOrientation = glm::quat_cast(headPose); + glm::vec3 hmdPosition = glm::vec3(headPose[3]); + + lookAtSpot = _myCamera.getPosition() + hmdPosition + + _myAvatar->getOrientation() * (hmdOrientation * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); } else { lookAtSpot = _myAvatar->getHead()->getEyePosition() + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); diff --git a/interface/src/Application.h b/interface/src/Application.h index 0d308e7a8f..239b440822 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -292,9 +292,10 @@ public: virtual QGLWidget* getPrimarySurface() override; void setActiveDisplayPlugin(const QString& pluginName); -private: + DisplayPlugin * getActiveDisplayPlugin(); const DisplayPlugin * getActiveDisplayPlugin() const; + public: FileLogger* getLogger() { return _logger; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 18c29d49fe..99973f2256 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1253,9 +1254,18 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } if (qApp->isHMDMode()) { + + // TODO: Test ... + // Look-at vectors should go from HMD mid eyes regardless of combined position and orientation + + glm::mat4 leftEye = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Left); + glm::vec3 leftEyePosition = glm::vec3(leftEye[3]); + glm::mat4 rightEye = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Right); + glm::vec3 rightEyePosition = glm::vec3(leftEye[3]); + glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); - getHead()->renderLookAts(renderArgs, cameraPosition + OculusManager::getLeftEyePosition(), - cameraPosition + OculusManager::getRightEyePosition()); + getHead()->renderLookAts(renderArgs, cameraPosition + leftEyePosition, + cameraPosition + rightEyePosition); } else { getHead()->renderLookAts(renderArgs); } From 0f56285283bc67e3f9521969dcebf18c1b4803c2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 11 Aug 2015 18:54:55 -0700 Subject: [PATCH 37/41] Fix HMD look-at positions and lines --- interface/src/Application.cpp | 24 ++++++------------------ interface/src/avatar/MyAvatar.cpp | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 23fc1f13e1..d8dad73d82 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2374,16 +2374,10 @@ void Application::updateMyAvatarLookAtPosition() { } else if (eyeTracker->isTracking() && (isHMDMode() || eyeTracker->isSimulating())) { // Look at the point that the user is looking at. if (isHMDMode()) { - - // TODO: Test ... - // Position of simulated look-at target should change with change in HMD position and orientation - glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); - glm::quat hmdOrientation = glm::quat_cast(headPose); - glm::vec3 hmdPosition = glm::vec3(headPose[3]); - - lookAtSpot = _myCamera.getPosition() + hmdPosition + - _myAvatar->getOrientation() * (hmdOrientation * eyeTracker->getLookAtPosition()); + glm::quat hmdRotation = glm::quat_cast(headPose); + lookAtSpot = _myCamera.getPosition() + + _myAvatar->getOrientation() * (hmdRotation * eyeTracker->getLookAtPosition()); } else { lookAtSpot = _myAvatar->getHead()->getEyePosition() + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * eyeTracker->getLookAtPosition()); @@ -2421,16 +2415,10 @@ void Application::updateMyAvatarLookAtPosition() { } else { // I am not looking at anyone else, so just look forward if (isHMDMode()) { - - // TODO: Test ... - // Look-at vector should go into distance based on HMD position and orientation - glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(); - glm::quat hmdOrientation = glm::quat_cast(headPose); - glm::vec3 hmdPosition = glm::vec3(headPose[3]); - - lookAtSpot = _myCamera.getPosition() + hmdPosition + - _myAvatar->getOrientation() * (hmdOrientation * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); + glm::quat headRotation = glm::quat_cast(headPose); + lookAtSpot = _myCamera.getPosition() + + _myAvatar->getOrientation() * (headRotation * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); } else { lookAtSpot = _myAvatar->getHead()->getEyePosition() + (_myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE)); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 99973f2256..5578ba76bd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1254,18 +1254,18 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl } if (qApp->isHMDMode()) { - - // TODO: Test ... - // Look-at vectors should go from HMD mid eyes regardless of combined position and orientation - - glm::mat4 leftEye = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Left); - glm::vec3 leftEyePosition = glm::vec3(leftEye[3]); - glm::mat4 rightEye = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Right); - glm::vec3 rightEyePosition = glm::vec3(leftEye[3]); - glm::vec3 cameraPosition = Application::getInstance()->getCamera()->getPosition(); - getHead()->renderLookAts(renderArgs, cameraPosition + leftEyePosition, - cameraPosition + rightEyePosition); + + glm::mat4 leftEyePose = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Left); + glm::vec3 leftEyePosition = glm::vec3(leftEyePose[3]); + glm::mat4 rightEyePose = Application::getInstance()->getActiveDisplayPlugin()->getEyePose(Eye::Right); + glm::vec3 rightEyePosition = glm::vec3(rightEyePose[3]); + glm::mat4 headPose = Application::getInstance()->getActiveDisplayPlugin()->getHeadPose(); + glm::vec3 headPosition = glm::vec3(headPose[3]); + + getHead()->renderLookAts(renderArgs, + cameraPosition + getOrientation() * (leftEyePosition - headPosition), + cameraPosition + getOrientation() * (rightEyePosition - headPosition)); } else { getHead()->renderLookAts(renderArgs); } From 8f376247f069b9503d1f6aa9b104ea508f0d5b44 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 11 Aug 2015 20:25:02 -0700 Subject: [PATCH 38/41] Fix calculation of corrected look-at position in HMD view --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5578ba76bd..6bd65701d3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -935,7 +935,7 @@ void MyAvatar::updateLookAtTargetAvatar() { if (Application::getInstance()->isHMDMode()) { avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() - + glm::vec3(qApp->getHMDSensorPose()[3]) + gazeOffset); + + gazeOffset); } else { avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() + gazeOffset); From 9fe1aaf1826479f158d45639665c8c2d8c00cb41 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 12 Aug 2015 15:21:06 -0700 Subject: [PATCH 39/41] Disable saving / loading eye tracker calibrations so can use old SDK --- interface/src/devices/EyeTracker.cpp | 47 +++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index bea04c16a2..8487ea02bc 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -151,20 +151,21 @@ void EyeTracker::onStreamStarted() { } } - if (_isStreaming) { - // Automatically load calibration if one has been saved. - QString availableCalibrations = QString(smi_getAvailableCalibrations()); - if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { - result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); - if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); - QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" - + smiReturnValueToString(result)); - } else { - qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; - } - } - } + // TODO: Re-enable once saving / loading calibrations is working + //if (_isStreaming) { + // // Automatically load calibration if one has been saved. + // QString availableCalibrations = QString(smi_getAvailableCalibrations()); + // if (availableCalibrations.contains(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION)) { + // result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + // if (result != SMI_RET_SUCCESS) { + // qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result); + // QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration" + // + smiReturnValueToString(result)); + // } else { + // qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration"; + // } + // } + //} } #endif @@ -257,10 +258,11 @@ void EyeTracker::calibrate(int points) { if (result != SMI_RET_SUCCESS) { qCWarning(interfaceapp) << "Eye Tracker: Error performing calibration:" << smiReturnValueToString(result); } else { - result = smi_saveCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); - if (result != SMI_RET_SUCCESS) { - qCWarning(interfaceapp) << "Eye Tracker: Error saving calibration:" << smiReturnValueToString(result); - } + // TODO: Re - enable once saving / loading calibrations is working + //result = smi_saveCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION); + //if (result != SMI_RET_SUCCESS) { + // qCWarning(interfaceapp) << "Eye Tracker: Error saving calibration:" << smiReturnValueToString(result); + //} } } @@ -288,10 +290,11 @@ QString EyeTracker::smiReturnValueToString(int value) { return "Eye cameras not available"; case smi_ErrorReturnValue::SMI_ERROR_OCULUS_RUNTIME_NOT_SUPPORTED: return "Oculus runtime not supported"; - case smi_ErrorReturnValue::SMI_ERROR_FILE_NOT_FOUND: - return "File not found"; - case smi_ErrorReturnValue::SMI_ERROR_FILE_EMPTY: - return "File empty"; + // TODO: Re-enable once saving / loading calibrations is working + //case smi_ErrorReturnValue::SMI_ERROR_FILE_NOT_FOUND: + // return "File not found"; + //case smi_ErrorReturnValue::SMI_ERROR_FILE_EMPTY: + // return "File empty"; case smi_ErrorReturnValue::SMI_ERROR_UNKNOWN: return "Unknown error"; default: From 7298fa7e2c5f23ebf15ebef5dc65f738bc79d020 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 12 Aug 2015 15:22:24 -0700 Subject: [PATCH 40/41] Log successful start of eye tracker streaming --- interface/src/devices/EyeTracker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/devices/EyeTracker.cpp b/interface/src/devices/EyeTracker.cpp index 8487ea02bc..532c1d41f3 100644 --- a/interface/src/devices/EyeTracker.cpp +++ b/interface/src/devices/EyeTracker.cpp @@ -149,6 +149,8 @@ void EyeTracker::onStreamStarted() { if (result != SMI_ERROR_HMD_NOT_SUPPORTED) { QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result)); } + } else { + qCDebug(interfaceapp) << "Eye Tracker: Started streaming"; } // TODO: Re-enable once saving / loading calibrations is working From 6a0a08eafb3bd45758a7adcdd82d51bdfc23dda5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 12 Aug 2015 15:25:34 -0700 Subject: [PATCH 41/41] Consolidate identical code paths --- interface/src/avatar/MyAvatar.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6bd65701d3..f51a077304 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -932,14 +932,8 @@ void MyAvatar::updateLookAtTargetAvatar() { const float HUMAN_EYE_SEPARATION = 0.065f; float myEyeSeparation = glm::length(getHead()->getLeftEyePosition() - getHead()->getRightEyePosition()); gazeOffset = gazeOffset * HUMAN_EYE_SEPARATION / myEyeSeparation; - - if (Application::getInstance()->isHMDMode()) { - avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() - + gazeOffset); - } else { - avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() - + gazeOffset); - } + avatar->getHead()->setCorrectedLookAtPosition(Application::getInstance()->getViewFrustum()->getPosition() + + gazeOffset); } else { avatar->getHead()->clearCorrectedLookAtPosition(); }