diff --git a/.gitignore b/.gitignore index eb2f3ec303..c296a918af 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,9 @@ interface/external/visage/* interface/resources/visage/* !interface/resources/visage/tracker.cfg +# Ignore Faceplus +interface/external/faceplus/* +!interface/external/faceplus/readme.txt + # Ignore interfaceCache for Linux users interface/interfaceCache/ diff --git a/cmake/modules/FindFaceplus.cmake b/cmake/modules/FindFaceplus.cmake new file mode 100644 index 0000000000..2a2083d6e4 --- /dev/null +++ b/cmake/modules/FindFaceplus.cmake @@ -0,0 +1,42 @@ +# Try to find the Faceplus library +# +# You must provide a FACEPLUS_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# FACEPLUS_FOUND - system found Faceplus +# FACEPLUS_INCLUDE_DIRS - the Faceplus include directory +# FACEPLUS_LIBRARIES - Link this to use Faceplus +# +# Created on 4/8/2014 by Andrzej Kapolka +# Copyright (c) 2014 High Fidelity +# + +if (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS) + # in cache already + set(FACEPLUS_FOUND TRUE) +else (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS) + find_path(FACEPLUS_INCLUDE_DIRS faceplus.h ${FACEPLUS_ROOT_DIR}/include) + + if (WIN32) + find_library(FACEPLUS_LIBRARIES faceplus.lib ${FACEPLUS_ROOT_DIR}/win32/) + endif (WIN32) + + if (FACEPLUS_INCLUDE_DIRS AND FACEPLUS_LIBRARIES) + set(FACEPLUS_FOUND TRUE) + endif (FACEPLUS_INCLUDE_DIRS AND FACEPLUS_LIBRARIES) + + if (FACEPLUS_FOUND) + if (NOT FACEPLUS_FIND_QUIETLY) + message(STATUS "Found Faceplus... ${FACEPLUS_LIBRARIES}") + endif (NOT FACEPLUS_FIND_QUIETLY) + else () + if (FACEPLUS_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Faceplus") + endif (FACEPLUS_FIND_REQUIRED) + endif () + + # show the FACEPLUS_INCLUDE_DIRS and FACEPLUS_LIBRARIES variables only in the advanced view + mark_as_advanced(FACEPLUS_INCLUDE_DIRS FACEPLUS_LIBRARIES) + +endif (FACEPLUS_LIBRARIES AND FACEPLUS_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 55d0a86d87..29943eb4cb 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -12,6 +12,7 @@ project(${TARGET_NAME}) # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake/modules/") +set(FACEPLUS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceplus") set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift") set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus") set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense") @@ -130,6 +131,7 @@ link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") # find any optional libraries +find_package(Faceplus) find_package(Faceshift) find_package(LibOVR) find_package(Sixense) @@ -163,6 +165,13 @@ if (VISAGE_FOUND AND NOT DISABLE_VISAGE) target_link_libraries(${TARGET_NAME} "${VISAGE_LIBRARIES}") endif (VISAGE_FOUND AND NOT DISABLE_VISAGE) +# and with Faceplus library, also for webcam feature tracking +if (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS) + add_definitions(-DHAVE_FACEPLUS) + include_directories(SYSTEM "${FACEPLUS_INCLUDE_DIRS}") + target_link_libraries(${TARGET_NAME} "${FACEPLUS_LIBRARIES}") +endif (FACEPLUS_FOUND AND NOT DISABLE_FACEPLUS) + # and with LibOVR for Oculus Rift if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) add_definitions(-DHAVE_LIBOVR) diff --git a/interface/external/faceplus/readme.txt b/interface/external/faceplus/readme.txt new file mode 100644 index 0000000000..e98f8becdc --- /dev/null +++ b/interface/external/faceplus/readme.txt @@ -0,0 +1,11 @@ + +Instructions for adding the Faceplus driver to Interface +Andrzej Kapolka, April 8, 2014 + +1. Copy the Faceplus sdk folders (include, win32) into the interface/external/faceplus folder. + This readme.txt should be there as well. + +2. Copy the Faceplus DLLs from the win32 folder into your path. + +3. Delete your build directory, run cmake and build, and you should be all set. + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6005ae085e..f6b0d37dd4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -150,7 +150,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _viewFrustum(), _lastQueriedViewFrustum(), _lastQueriedTime(usecTimestampNow()), - _audioScope(256, 200, true), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), _mouseX(0), _mouseY(0), @@ -161,7 +160,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _touchAvgY(0.0f), _isTouchPressed(false), _mousePressed(false), - _audio(&_audioScope, STARTUP_JITTER_SAMPLES), + _audio(STARTUP_JITTER_SAMPLES), _enableProcessVoxelsThread(true), _voxelProcessor(), _voxelHideShowThread(&_voxels), @@ -342,11 +341,15 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // clear the scripts, and set out script to our default scripts clearScriptsBeforeRunning(); loadScript("http://public.highfidelity.io/scripts/defaultScripts.js"); - + + QMutexLocker locker(&_settingsMutex); _settings->setValue("firstRun",QVariant(false)); } else { // do this as late as possible so that all required subsystems are inialized loadScripts(); + + QMutexLocker locker(&_settingsMutex); + _previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString(); } } @@ -356,16 +359,12 @@ Application::~Application() { // make sure we don't call the idle timer any more delete idleTimer; - - Menu::getInstance()->saveSettings(); - _rearMirrorTools->saveSettings(_settings); - + _sharedVoxelSystem.changeTree(new VoxelTree); - if (_voxelImporter) { - _voxelImporter->saveSettings(_settings); - delete _voxelImporter; - } - _settings->sync(); + + saveSettings(); + + delete _voxelImporter; // let the avatar mixer know we're out MyAvatar::sendKillAvatar(); @@ -399,35 +398,45 @@ Application::~Application() { AccountManager::getInstance().destroy(); } +void Application::saveSettings() { + Menu::getInstance()->saveSettings(); + _rearMirrorTools->saveSettings(_settings); + + if (_voxelImporter) { + _voxelImporter->saveSettings(_settings); + } + _settings->sync(); +} + + void Application::restoreSizeAndPosition() { - QSettings* settings = new QSettings(this); QRect available = desktop()->availableGeometry(); - settings->beginGroup("Window"); + QMutexLocker locker(&_settingsMutex); + _settings->beginGroup("Window"); - int x = (int)loadSetting(settings, "x", 0); - int y = (int)loadSetting(settings, "y", 0); + int x = (int)loadSetting(_settings, "x", 0); + int y = (int)loadSetting(_settings, "y", 0); _window->move(x, y); - int width = (int)loadSetting(settings, "width", available.width()); - int height = (int)loadSetting(settings, "height", available.height()); + int width = (int)loadSetting(_settings, "width", available.width()); + int height = (int)loadSetting(_settings, "height", available.height()); _window->resize(width, height); - settings->endGroup(); + _settings->endGroup(); } void Application::storeSizeAndPosition() { - QSettings* settings = new QSettings(this); + QMutexLocker locker(&_settingsMutex); + _settings->beginGroup("Window"); - settings->beginGroup("Window"); + _settings->setValue("width", _window->rect().width()); + _settings->setValue("height", _window->rect().height()); - settings->setValue("width", _window->rect().width()); - settings->setValue("height", _window->rect().height()); + _settings->setValue("x", _window->pos().x()); + _settings->setValue("y", _window->pos().y()); - settings->setValue("x", _window->pos().x()); - settings->setValue("y", _window->pos().y()); - - settings->endGroup(); + _settings->endGroup(); } void Application::initializeGL() { @@ -744,9 +753,6 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Period: Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key()); break; - case Qt::Key_Apostrophe: - _audioScope.inputPaused = !_audioScope.inputPaused; - break; case Qt::Key_L: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::LodTools); @@ -1345,6 +1351,12 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox (mouseVoxel.z + mouseVoxel.s / 2.f) * TREE_SCALE); } +FaceTracker* Application::getActiveFaceTracker() { + return _faceshift.isActive() ? static_cast(&_faceshift) : + (_faceplus.isActive() ? static_cast(&_faceplus) : + (_visage.isActive() ? static_cast(&_visage) : NULL)); +} + struct SendVoxelsOperationArgs { const unsigned char* newBaseOctCode; }; @@ -1564,8 +1576,9 @@ void Application::init() { } qDebug("Loaded settings"); - // initialize Visage and Faceshift after loading the menu settings + // initialize our face trackers after loading the menu settings _faceshift.init(); + _faceplus.init(); _visage.init(); // fire off an immediate domain-server check in now that settings are loaded @@ -1729,19 +1742,11 @@ void Application::updateMyAvatarLookAtPosition() { glm::distance(_mouseRayOrigin, _myAvatar->getHead()->calculateAverageEyePosition())); lookAtSpot = _mouseRayOrigin + _mouseRayDirection * qMax(minEyeDistance, distance); } - bool trackerActive = false; - float eyePitch, eyeYaw; - if (_faceshift.isActive()) { - eyePitch = _faceshift.getEstimatedEyePitch(); - eyeYaw = _faceshift.getEstimatedEyeYaw(); - trackerActive = true; - - } else if (_visage.isActive()) { - eyePitch = _visage.getEstimatedEyePitch(); - eyeYaw = _visage.getEstimatedEyeYaw(); - trackerActive = true; - } - if (trackerActive) { + FaceTracker* tracker = getActiveFaceTracker(); + if (tracker) { + float eyePitch = tracker->getEstimatedEyePitch(); + float eyeYaw = tracker->getEstimatedEyeYaw(); + // deflect using Faceshift gaze data glm::vec3 origin = _myAvatar->getHead()->calculateAverageEyePosition(); float pitchSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? -1.0f : 1.0f; @@ -1827,15 +1832,15 @@ void Application::updateCamera(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateCamera()"); - if (!OculusManager::isConnected() && !TV3DManager::isConnected()) { - if (Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) { - float xSign = _myCamera.getMode() == CAMERA_MODE_MIRROR ? 1.0f : -1.0f; - if (_faceshift.isActive()) { - const float EYE_OFFSET_SCALE = 0.025f; - glm::vec3 position = _faceshift.getHeadTranslation() * EYE_OFFSET_SCALE; - _myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z)); - updateProjectionMatrix(); - } + if (!OculusManager::isConnected() && !TV3DManager::isConnected() && + Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) { + FaceTracker* tracker = getActiveFaceTracker(); + if (tracker) { + const float EYE_OFFSET_SCALE = 0.025f; + glm::vec3 position = tracker->getHeadTranslation() * EYE_OFFSET_SCALE; + float xSign = (_myCamera.getMode() == CAMERA_MODE_MIRROR) ? 1.0f : -1.0f; + _myCamera.setEyeOffsetPosition(glm::vec3(position.x * xSign, position.y, -position.z)); + updateProjectionMatrix(); } } } @@ -2509,15 +2514,6 @@ void Application::displayOverlay() { } } - // Audio Scope - const int AUDIO_SCOPE_Y_OFFSET = 135; - if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { - int oscilloscopeTop = _glWidget->height() - AUDIO_SCOPE_Y_OFFSET; - _audioScope.render(MIRROR_VIEW_LEFT_PADDING, oscilloscopeTop); - } - } - // Audio VU Meter and Mute Icon const int MUTE_ICON_SIZE = 24; const int AUDIO_METER_INSET = 2; @@ -3181,35 +3177,36 @@ void Application::packetSent(quint64 length) { void Application::loadScripts() { // loads all saved scripts - QSettings* settings = new QSettings(this); - int size = settings->beginReadArray("Settings"); - + int size = lockSettings()->beginReadArray("Settings"); + unlockSettings(); for (int i = 0; i < size; ++i){ - settings->setArrayIndex(i); - QString string = settings->value("script").toString(); - loadScript(string); + lockSettings()->setArrayIndex(i); + QString string = _settings->value("script").toString(); + unlockSettings(); + if (!string.isEmpty()) { + loadScript(string); + } } - - settings->endArray(); + + QMutexLocker locker(&_settingsMutex); + _settings->endArray(); } void Application::clearScriptsBeforeRunning() { // clears all scripts from the settings - QSettings* settings = new QSettings(this); - settings->beginWriteArray("Settings"); - settings->endArray(); + QMutexLocker locker(&_settingsMutex); + _settings->remove("Settings"); } void Application::saveScripts() { // saves all current running scripts - QSettings* settings = new QSettings(this); - settings->beginWriteArray("Settings"); + QMutexLocker locker(&_settingsMutex); + _settings->beginWriteArray("Settings"); for (int i = 0; i < getRunningScripts().size(); ++i){ - settings->setArrayIndex(i); - settings->setValue("script", getRunningScripts().at(i)); + _settings->setArrayIndex(i); + _settings->setValue("script", getRunningScripts().at(i)); } - - settings->endArray(); + _settings->endArray(); } void Application::stopAllScripts() { @@ -3350,7 +3347,10 @@ void Application::loadDialog() { if (_previousScriptLocation.isEmpty()) { QString desktopLocation = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475 +#ifdef __APPLE__ suggestedName = desktopLocation.append("/script.js"); +#endif } else { suggestedName = _previousScriptLocation; } @@ -3359,9 +3359,11 @@ void Application::loadDialog() { tr("JavaScript Files (*.js)")); if (!fileNameString.isEmpty()) { _previousScriptLocation = fileNameString; + QMutexLocker locker(&_settingsMutex); + _settings->setValue("LastScriptLocation", _previousScriptLocation); + + loadScript(fileNameString); } - - loadScript(fileNameString); } void Application::loadScriptURLDialog() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 61c2a15f95..eedb866d53 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -52,6 +52,7 @@ #include "avatar/Avatar.h" #include "avatar/AvatarManager.h" #include "avatar/MyAvatar.h" +#include "devices/Faceplus.h" #include "devices/Faceshift.h" #include "devices/SixenseManager.h" #include "devices/Visage.h" @@ -176,8 +177,10 @@ public: bool isMouseHidden() const { return _mouseHidden; } const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } + Faceplus* getFaceplus() { return &_faceplus; } Faceshift* getFaceshift() { return &_faceshift; } Visage* getVisage() { return &_visage; } + FaceTracker* getActiveFaceTracker(); SixenseManager* getSixenseManager() { return &_sixenseManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QUndoStack* getUndoStack() { return &_undoStack; } @@ -185,6 +188,8 @@ public: /// if you need to access the application settings, use lockSettings()/unlockSettings() QSettings* lockSettings() { _settingsMutex.lock(); return _settings; } void unlockSettings() { _settingsMutex.unlock(); } + + void saveSettings(); QMainWindow* getWindow() { return _window; } NodeToOctreeSceneStats* getOcteeSceneStats() { return &_octreeServerSceneStats; } @@ -316,6 +321,7 @@ private: // Various helper functions called during update() void updateLOD(); void updateMouseRay(); + void updateFaceplus(); void updateFaceshift(); void updateVisage(); void updateMyAvatarLookAtPosition(); @@ -407,7 +413,6 @@ private: ViewFrustum _shadowViewFrustum; quint64 _lastQueriedTime; - Oscilloscope _audioScope; float _trailingAudioLoudness; OctreeQuery _octreeQuery; // NodeData derived class for querying voxels from voxel server @@ -415,9 +420,10 @@ private: AvatarManager _avatarManager; MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be) + Faceplus _faceplus; Faceshift _faceshift; Visage _visage; - + SixenseManager _sixenseManager; Camera _myCamera; // My view onto the world diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index a98d276acc..5dcd54050c 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -50,7 +50,7 @@ static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300; // Mute icon configration static const int MUTE_ICON_SIZE = 24; -Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent) : +Audio::Audio(int16_t initialJitterBufferSamples, QObject* parent) : AbstractAudioInterface(parent), _audioInput(NULL), _desiredInputFormat(), @@ -67,7 +67,6 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* p _proceduralOutputDevice(NULL), _inputRingBuffer(0), _ringBuffer(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL), - _scope(scope), _averagedLatency(0.0), _measuredJitter(0), _jitterBufferSamples(initialJitterBufferSamples), @@ -555,12 +554,6 @@ void Audio::handleAudioInput() { _lastInputLoudness = 0; } } - - // add input data just written to the scope - QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection, - Q_ARG(QByteArray, QByteArray((char*) monoAudioSamples, - NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL)), - Q_ARG(bool, false), Q_ARG(bool, true)); } else { // our input loudness is 0, since we're muted _lastInputLoudness = 0; @@ -724,11 +717,6 @@ void Audio::processReceivedAudio(const QByteArray& audioByteArray) { if (_outputDevice) { _outputDevice->write(outputBuffer); - - // add output (@speakers) data just written to the scope - QMetaObject::invokeMethod(_scope, "addSamples", Qt::QueuedConnection, - Q_ARG(QByteArray, QByteArray((char*) ringBufferSamples, numNetworkOutputSamples)), - Q_ARG(bool, true), Q_ARG(bool, false)); } delete[] ringBufferSamples; } diff --git a/interface/src/Audio.h b/interface/src/Audio.h index c529fc2651..bfb3450d72 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -34,9 +34,6 @@ #include #include -#include "ui/Oscilloscope.h" - - static const int NUM_AUDIO_CHANNELS = 2; class QAudioInput; @@ -47,7 +44,7 @@ class Audio : public AbstractAudioInterface { Q_OBJECT public: // setup for audio I/O - Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples, QObject* parent = 0); + Audio(int16_t initialJitterBufferSamples, QObject* parent = 0); float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); } float getTimeSinceLastClip() const { return _timeSinceLastClip; } @@ -126,7 +123,6 @@ private: QString _inputAudioDeviceName; QString _outputAudioDeviceName; - Oscilloscope* _scope; StDev _stdev; timeval _lastReceiveTime; float _averagedLatency; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 70e69597f6..8eead4acdc 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -252,7 +252,6 @@ Menu::Menu() : addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Oscilloscope, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails())); @@ -304,6 +303,11 @@ Menu::Menu() : true, appInstance->getFaceshift(), SLOT(setTCPEnabled(bool))); +#ifdef HAVE_FACEPLUS + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Faceplus, 0, true, + appInstance->getFaceplus(), SLOT(updateEnabled())); +#endif + #ifdef HAVE_VISAGE addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, true, appInstance->getVisage(), SLOT(updateEnabled())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 5bc48f916f..fb71efa5e5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -253,6 +253,7 @@ namespace MenuOption { const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; const QString Enable3DTVMode = "Enable 3DTV Mode"; + const QString Faceplus = "Faceplus"; const QString Faceshift = "Faceshift"; const QString FilterSixense = "Smooth Sixense Movement"; const QString FirstPerson = "First Person"; @@ -287,7 +288,6 @@ namespace MenuOption { const QString OctreeStats = "Voxel and Particle Statistics"; const QString OffAxisProjection = "Off-Axis Projection"; const QString OldVoxelCullingMode = "Old Voxel Culling Mode"; - const QString Oscilloscope = "Audio Oscilloscope"; const QString Pair = "Pair"; const QString Particles = "Particles"; const QString PasteToVoxel = "Paste to Voxel..."; diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 9c4755e388..d22097a00c 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -22,7 +22,9 @@ #include +#include "Application.h" #include "renderer/FBXReader.h" + #include "ModelUploader.h" @@ -34,6 +36,8 @@ static const QString LOD_FIELD = "lod"; static const QString S3_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com"; static const QString MODEL_URL = "/api/v1/models"; +static const QString SETTING_NAME = "LastModelUploadLocation"; + static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB static const int TIMEOUT = 1000; static const int MAX_CHECK = 30; @@ -59,14 +63,27 @@ ModelUploader::~ModelUploader() { bool ModelUploader::zip() { // File Dialog - QString filename = QFileDialog::getOpenFileName(NULL, - "Select your .fst file ...", - QStandardPaths::writableLocation(QStandardPaths::HomeLocation), - "*.fst"); + QSettings* settings = Application::getInstance()->lockSettings(); + QString lastLocation = settings->value(SETTING_NAME).toString(); + + if (lastLocation.isEmpty()) { + lastLocation = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + // Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475 +#ifdef __APPLE__ + lastLocation.append("/model.fst"); +#endif + } + + + QString filename = QFileDialog::getOpenFileName(NULL, "Select your .fst file ...", lastLocation, "*.fst"); if (filename == "") { // If the user canceled we return. + Application::getInstance()->unlockSettings(); return false; } + settings->setValue(SETTING_NAME, filename); + Application::getInstance()->unlockSettings(); + bool _nameIsPresent = false; QString texDir; QString fbxFile; diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 8316c90aca..a213603a89 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -64,17 +64,11 @@ void Head::reset() { void Head::simulate(float deltaTime, bool isMine, bool billboard) { // Update audio trailing average for rendering facial animations - Faceshift* faceshift = Application::getInstance()->getFaceshift(); - Visage* visage = Application::getInstance()->getVisage(); if (isMine) { - _isFaceshiftConnected = false; - if (faceshift->isActive()) { - _blendshapeCoefficients = faceshift->getBlendshapeCoefficients(); - _isFaceshiftConnected = true; - - } else if (visage->isActive()) { - _blendshapeCoefficients = visage->getBlendshapeCoefficients(); - _isFaceshiftConnected = true; + FaceTracker* faceTracker = Application::getInstance()->getActiveFaceTracker(); + if ((_isFaceshiftConnected = faceTracker)) { + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + _isFaceshiftConnected = true; } } @@ -156,8 +150,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { const float BROW_LIFT_SCALE = 500.0f; const float JAW_OPEN_SCALE = 0.01f; const float JAW_OPEN_DEAD_ZONE = 0.75f; - faceshift->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, min(1.0f, _browAudioLift * BROW_LIFT_SCALE), - glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients); + Application::getInstance()->getFaceshift()->updateFakeCoefficients(_leftEyeBlink, _rightEyeBlink, + min(1.0f, _browAudioLift * BROW_LIFT_SCALE), glm::clamp(sqrt(_averageLoudness * JAW_OPEN_SCALE) - + JAW_OPEN_DEAD_ZONE, 0.0f, 1.0f), _blendshapeCoefficients); } if (!isMine) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 72c8544165..7a05c593db 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -65,7 +65,8 @@ MyAvatar::MyAvatar() : _moveTargetStepCounter(0), _lookAtTargetAvatar(), _shouldRender(true), - _billboardValid(false) + _billboardValid(false), + _oculusYawOffset(0.0f) { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; @@ -88,10 +89,11 @@ void MyAvatar::reset() { _skeletonModel.reset(); getHead()->reset(); getHand()->reset(); + _oculusYawOffset = 0.0f; - setVelocity(glm::vec3(0.f)); - setThrust(glm::vec3(0.f)); - setOrientation(glm::quat(glm::vec3(0.f))); + setVelocity(glm::vec3(0.0f)); + setThrust(glm::vec3(0.0f)); + setOrientation(glm::quat(glm::vec3(0.0f))); } void MyAvatar::setMoveTarget(const glm::vec3 moveTarget) { @@ -114,8 +116,8 @@ void MyAvatar::update(float deltaTime) { // TODO? resurrect headMouse stuff? //glm::vec3 headVelocity = faceshift->getHeadAngularVelocity(); //// sets how quickly head angular rotation moves the head mouse - //const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.f; - //const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.f; + //const float HEADMOUSE_FACESHIFT_YAW_SCALE = 40.0f; + //const float HEADMOUSE_FACESHIFT_PITCH_SCALE = 30.0f; //_headMouseX -= headVelocity.y * HEADMOUSE_FACESHIFT_YAW_SCALE; //_headMouseY -= headVelocity.x * HEADMOUSE_FACESHIFT_PITCH_SCALE; // @@ -124,16 +126,6 @@ void MyAvatar::update(float deltaTime) { //_headMouseY = glm::clamp(_headMouseY, 0, _glWidget->height()); } - if (OculusManager::isConnected()) { - float yaw, pitch, roll; // these angles will be in radians - OculusManager::getEulerAngles(yaw, pitch, roll); - - // but these euler angles are stored in degrees - head->setBaseYaw(yaw * DEGREES_PER_RADIAN); - head->setBasePitch(pitch * DEGREES_PER_RADIAN); - head->setBaseRoll(roll * DEGREES_PER_RADIAN); - } - // Get audio loudness data from audio input device Audio* audio = Application::getInstance()->getAudio(); head->setAudioLoudness(audio->getLastInputLoudness()); @@ -156,15 +148,15 @@ void MyAvatar::simulate(float deltaTime) { _elapsedTimeSinceCollision += deltaTime; const float VELOCITY_MOVEMENT_TIMER_THRESHOLD = 0.2f; if (glm::length(_velocity) < VELOCITY_MOVEMENT_TIMER_THRESHOLD) { - _elapsedTimeMoving = 0.f; + _elapsedTimeMoving = 0.0f; _elapsedTimeStopped += deltaTime; } else { - _elapsedTimeStopped = 0.f; + _elapsedTimeStopped = 0.0f; _elapsedTimeMoving += deltaTime; } if (_scale != _targetScale) { - float scale = (1.f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; + float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; setScale(scale); Application::getInstance()->getCamera()->setScale(scale); } @@ -192,19 +184,19 @@ void MyAvatar::simulate(float deltaTime) { // decay body rotation momentum const float BODY_SPIN_FRICTION = 7.5f; - float bodySpinMomentum = 1.f - BODY_SPIN_FRICTION * deltaTime; + float bodySpinMomentum = 1.0f - BODY_SPIN_FRICTION * deltaTime; if (bodySpinMomentum < 0.0f) { bodySpinMomentum = 0.0f; } _bodyPitchDelta *= bodySpinMomentum; _bodyYawDelta *= bodySpinMomentum; _bodyRollDelta *= bodySpinMomentum; float MINIMUM_ROTATION_RATE = 2.0f; - if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.f; } - if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.f; } - if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.f; } + if (fabs(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { _bodyYawDelta = 0.0f; } + if (fabs(_bodyRollDelta) < MINIMUM_ROTATION_RATE) { _bodyRollDelta = 0.0f; } + if (fabs(_bodyPitchDelta) < MINIMUM_ROTATION_RATE) { _bodyPitchDelta = 0.0f; } const float MAX_STATIC_FRICTION_SPEED = 0.5f; - const float STATIC_FRICTION_STRENGTH = _scale * 20.f; + const float STATIC_FRICTION_STRENGTH = _scale * 20.0f; applyStaticFriction(deltaTime, _velocity, MAX_STATIC_FRICTION_SPEED, STATIC_FRICTION_STRENGTH); // Damp avatar velocity @@ -212,11 +204,11 @@ void MyAvatar::simulate(float deltaTime) { const float SPEED_BRAKE_POWER = _scale * 10.0f; const float SQUARED_DAMPING_STRENGTH = 0.007f; - const float SLOW_NEAR_RADIUS = 5.f; + const float SLOW_NEAR_RADIUS = 5.0f; float linearDamping = LINEAR_DAMPING_STRENGTH; - const float NEAR_AVATAR_DAMPING_FACTOR = 50.f; + const float NEAR_AVATAR_DAMPING_FACTOR = 50.0f; if (_distanceToNearestAvatar < _scale * SLOW_NEAR_RADIUS) { - linearDamping *= 1.f + NEAR_AVATAR_DAMPING_FACTOR * + linearDamping *= 1.0f + NEAR_AVATAR_DAMPING_FACTOR * ((SLOW_NEAR_RADIUS - _distanceToNearestAvatar) / SLOW_NEAR_RADIUS); } if (_speedBrakes) { @@ -225,6 +217,54 @@ void MyAvatar::simulate(float deltaTime) { applyDamping(deltaTime, _velocity, linearDamping, SQUARED_DAMPING_STRENGTH); } + if (OculusManager::isConnected()) { + // these angles will be in radians + float yaw, pitch, roll; + OculusManager::getEulerAngles(yaw, pitch, roll); + // ... so they need to be converted to degrees before we do math... + + // The neck is limited in how much it can yaw, so we check its relative + // yaw from the body and yaw the body if necessary. + yaw *= DEGREES_PER_RADIAN; + float bodyToHeadYaw = yaw - _oculusYawOffset; + const float MAX_NECK_YAW = 85.0f; // degrees + if ((fabs(bodyToHeadYaw) > 2.0f * MAX_NECK_YAW) && (yaw * _oculusYawOffset < 0.0f)) { + // We've wrapped around the range for yaw so adjust + // the measured yaw to be relative to _oculusYawOffset. + if (yaw > 0.0f) { + yaw -= 360.0f; + } else { + yaw += 360.0f; + } + bodyToHeadYaw = yaw - _oculusYawOffset; + } + + float delta = fabs(bodyToHeadYaw) - MAX_NECK_YAW; + if (delta > 0.0f) { + yaw = MAX_NECK_YAW; + if (bodyToHeadYaw < 0.0f) { + delta *= -1.0f; + bodyToHeadYaw = -MAX_NECK_YAW; + } else { + bodyToHeadYaw = MAX_NECK_YAW; + } + // constrain _oculusYawOffset to be within range [-180,180] + _oculusYawOffset = fmod((_oculusYawOffset + delta) + 180.0f, 360.0f) - 180.0f; + + // We must adjust the body orientation using a delta rotation (rather than + // doing yaw math) because the body's yaw ranges are not the same + // as what the Oculus API provides. + glm::vec3 UP_AXIS = glm::vec3(0.0f, 1.0f, 0.0f); + glm::quat bodyCorrection = glm::angleAxis(glm::radians(delta), UP_AXIS); + orientation = orientation * bodyCorrection; + } + Head* head = getHead(); + head->setBaseYaw(bodyToHeadYaw); + + head->setBasePitch(pitch * DEGREES_PER_RADIAN); + head->setBaseRoll(roll * DEGREES_PER_RADIAN); + } + // update the euler angles setOrientation(orientation); @@ -243,7 +283,7 @@ void MyAvatar::simulate(float deltaTime) { // If a move target is set, update position explicitly const float MOVE_FINISHED_TOLERANCE = 0.1f; - const float MOVE_SPEED_FACTOR = 2.f; + const float MOVE_SPEED_FACTOR = 2.0f; const int MOVE_TARGET_MAX_STEPS = 250; if ((glm::length(_moveTarget) > EPSILON) && (_moveTargetStepCounter < MOVE_TARGET_MAX_STEPS)) { if (glm::length(_position - _moveTarget) > MOVE_FINISHED_TOLERANCE) { @@ -283,7 +323,7 @@ void MyAvatar::simulate(float deltaTime) { head->simulate(deltaTime, true); // Zero thrust out now that we've added it to velocity in this frame - _thrust = glm::vec3(0.f); + _thrust = glm::vec3(0.0f); // now that we're done stepping the avatar forward in time, compute new collisions if (_collisionFlags != 0) { @@ -291,7 +331,7 @@ void MyAvatar::simulate(float deltaTime) { float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { - radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); + radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f)); radius *= COLLISION_RADIUS_SCALAR; } @@ -312,45 +352,35 @@ void MyAvatar::simulate(float deltaTime) { // Update avatar head rotation with sensor data void MyAvatar::updateFromGyros(float deltaTime) { - Faceshift* faceshift = Application::getInstance()->getFaceshift(); - Visage* visage = Application::getInstance()->getVisage(); glm::vec3 estimatedPosition, estimatedRotation; - bool trackerActive = false; - if (faceshift->isActive()) { - estimatedPosition = faceshift->getHeadTranslation(); - estimatedRotation = glm::degrees(safeEulerAngles(faceshift->getHeadRotation())); - trackerActive = true; - - } else if (visage->isActive()) { - estimatedPosition = visage->getHeadTranslation(); - estimatedRotation = glm::degrees(safeEulerAngles(visage->getHeadRotation())); - trackerActive = true; - } - - Head* head = getHead(); - if (trackerActive) { + FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); + if (tracker) { + estimatedPosition = tracker->getHeadTranslation(); + estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); + // Rotate the body if the head is turned beyond the screen if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; - const float TRACKER_MIN_YAW_TURN = 15.f; - const float TRACKER_MAX_YAW_TURN = 50.f; + const float TRACKER_MIN_YAW_TURN = 15.0f; + const float TRACKER_MAX_YAW_TURN = 50.0f; if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) && (fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) { - if (estimatedRotation.y > 0.f) { + if (estimatedRotation.y > 0.0f) { _bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; } else { _bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; } } } - } + } // Set the rotation of the avatar's head (as seen by others, not affecting view frustum) // to be scaled. Pitch is greater to emphasize nodding behavior / synchrony. const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f; const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f; const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f; + Head* head = getHead(); head->setDeltaPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY); head->setDeltaYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY); head->setDeltaRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY); @@ -375,20 +405,20 @@ void MyAvatar::moveWithLean() { float leanSideways = head->getLeanSideways(); // Degrees of 'dead zone' when leaning, and amount of acceleration to apply to lean angle - const float LEAN_FWD_DEAD_ZONE = 15.f; - const float LEAN_SIDEWAYS_DEAD_ZONE = 10.f; - const float LEAN_FWD_THRUST_SCALE = 4.f; - const float LEAN_SIDEWAYS_THRUST_SCALE = 3.f; + const float LEAN_FWD_DEAD_ZONE = 15.0f; + const float LEAN_SIDEWAYS_DEAD_ZONE = 10.0f; + const float LEAN_FWD_THRUST_SCALE = 4.0f; + const float LEAN_SIDEWAYS_THRUST_SCALE = 3.0f; if (fabs(leanForward) > LEAN_FWD_DEAD_ZONE) { - if (leanForward > 0.f) { + if (leanForward > 0.0f) { addThrust(front * -(leanForward - LEAN_FWD_DEAD_ZONE) * LEAN_FWD_THRUST_SCALE); } else { addThrust(front * -(leanForward + LEAN_FWD_DEAD_ZONE) * LEAN_FWD_THRUST_SCALE); } } if (fabs(leanSideways) > LEAN_SIDEWAYS_DEAD_ZONE) { - if (leanSideways > 0.f) { + if (leanSideways > 0.0f) { addThrust(right * -(leanSideways - LEAN_SIDEWAYS_DEAD_ZONE) * LEAN_SIDEWAYS_THRUST_SCALE); } else { addThrust(right * -(leanSideways + LEAN_SIDEWAYS_DEAD_ZONE) * LEAN_SIDEWAYS_THRUST_SCALE); @@ -435,7 +465,7 @@ void MyAvatar::renderHeadMouse() const { // TODO? resurrect headMouse stuff? /* // Display small target box at center or head mouse target that can also be used to measure LOD - glColor3f(1.f, 1.f, 1.f); + glColor3f(1.0f, 1.0f, 1.0f); glDisable(GL_LINE_SMOOTH); const int PIXEL_BOX = 16; glBegin(GL_LINES); @@ -445,7 +475,7 @@ void MyAvatar::renderHeadMouse() const { glVertex2f(_headMouseX, _headMouseY + PIXEL_BOX/2); glEnd(); glEnable(GL_LINE_SMOOTH); - glColor3f(1.f, 0.f, 0.f); + glColor3f(1.0f, 0.0f, 0.0f); glPointSize(3.0f); glDisable(GL_POINT_SMOOTH); glBegin(GL_POINTS); @@ -457,7 +487,7 @@ void MyAvatar::renderHeadMouse() const { int eyeTargetX = (_glWidget->width() / 2) - _faceshift.getEstimatedEyeYaw() * EYE_TARGET_PIXELS_PER_DEGREE; int eyeTargetY = (_glWidget->height() / 2) - _faceshift.getEstimatedEyePitch() * EYE_TARGET_PIXELS_PER_DEGREE; - glColor3f(0.f, 1.f, 1.f); + glColor3f(0.0f, 1.0f, 1.0f); glDisable(GL_LINE_SMOOTH); glBegin(GL_LINES); glVertex2f(eyeTargetX - PIXEL_BOX/2, eyeTargetY); @@ -578,11 +608,6 @@ void MyAvatar::clearLookAtTargetAvatar() { _lookAtTargetAvatar.clear(); } -float MyAvatar::getAbsoluteHeadYaw() const { - const Head* head = static_cast(_headData); - return glm::yaw(head->getOrientation()); -} - glm::vec3 MyAvatar::getUprightHeadPosition() const { return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); } @@ -641,11 +666,11 @@ void MyAvatar::updateThrust(float deltaTime) { glm::vec3 up = orientation * IDENTITY_UP; const float THRUST_MAG_UP = 800.0f; - const float THRUST_MAG_DOWN = 300.f; - const float THRUST_MAG_FWD = 500.f; - const float THRUST_MAG_BACK = 300.f; - const float THRUST_MAG_LATERAL = 250.f; - const float THRUST_JUMP = 120.f; + const float THRUST_MAG_DOWN = 300.0f; + const float THRUST_MAG_FWD = 500.0f; + const float THRUST_MAG_BACK = 300.0f; + const float THRUST_MAG_LATERAL = 250.0f; + const float THRUST_JUMP = 120.0f; // Add Thrusts from keyboard _thrust += _driveKeys[FWD] * _scale * THRUST_MAG_FWD * _thrustMultiplier * deltaTime * front; @@ -656,25 +681,25 @@ void MyAvatar::updateThrust(float deltaTime) { _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; // attenuate thrust when in penetration - if (glm::dot(_thrust, _lastBodyPenetration) > 0.f) { + if (glm::dot(_thrust, _lastBodyPenetration) > 0.0f) { const float MAX_BODY_PENETRATION_DEPTH = 0.6f * _skeletonModel.getBoundingShapeRadius(); - float penetrationFactor = glm::min(1.f, glm::length(_lastBodyPenetration) / MAX_BODY_PENETRATION_DEPTH); + float penetrationFactor = glm::min(1.0f, glm::length(_lastBodyPenetration) / MAX_BODY_PENETRATION_DEPTH); glm::vec3 penetrationDirection = glm::normalize(_lastBodyPenetration); // attenuate parallel component glm::vec3 parallelThrust = glm::dot(_thrust, penetrationDirection) * penetrationDirection; // attenuate perpendicular component (friction) glm::vec3 perpendicularThrust = _thrust - parallelThrust; // recombine to get the final thrust - _thrust = (1.f - penetrationFactor) * parallelThrust + (1.f - penetrationFactor * penetrationFactor) * perpendicularThrust; + _thrust = (1.0f - penetrationFactor) * parallelThrust + (1.0f - penetrationFactor * penetrationFactor) * perpendicularThrust; // attenuate the growth of _thrustMultiplier when in penetration // otherwise the avatar will eventually be able to tunnel through the obstacle - _thrustMultiplier *= (1.f - penetrationFactor * penetrationFactor); - } else if (_thrustMultiplier < 1.f) { + _thrustMultiplier *= (1.0f - penetrationFactor * penetrationFactor); + } else if (_thrustMultiplier < 1.0f) { // rapid healing of attenuated thrustMultiplier after penetration event - _thrustMultiplier = 1.f; + _thrustMultiplier = 1.0f; } - _lastBodyPenetration = glm::vec3(0.f); + _lastBodyPenetration = glm::vec3(0.0f); _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_SPEED * deltaTime; @@ -684,12 +709,12 @@ void MyAvatar::updateThrust(float deltaTime) { if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { const float THRUST_INCREASE_RATE = 1.05f; const float MAX_THRUST_MULTIPLIER = 75.0f; - _thrustMultiplier *= 1.f + deltaTime * THRUST_INCREASE_RATE; + _thrustMultiplier *= 1.0f + deltaTime * THRUST_INCREASE_RATE; if (_thrustMultiplier > MAX_THRUST_MULTIPLIER) { _thrustMultiplier = MAX_THRUST_MULTIPLIER; } } else { - _thrustMultiplier = 1.f; + _thrustMultiplier = 1.0f; } // Add one time jumping force if requested @@ -794,11 +819,11 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity if (penetrationLength > EPSILON) { _elapsedTimeSinceCollision = 0.0f; glm::vec3 direction = penetration / penetrationLength; - _velocity -= glm::dot(_velocity, direction) * direction * (1.f + elasticity); - _velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f); - if ((glm::length(_velocity) < HALTING_VELOCITY) && (glm::length(_thrust) == 0.f)) { + _velocity -= glm::dot(_velocity, direction) * direction * (1.0f + elasticity); + _velocity *= glm::clamp(1.0f - damping, 0.0f, 1.0f); + if ((glm::length(_velocity) < HALTING_VELOCITY) && (glm::length(_thrust) == 0.0f)) { // If moving really slowly after a collision, and not applying forces, stop altogether - _velocity *= 0.f; + _velocity *= 0.0f; } } } @@ -806,7 +831,7 @@ void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) { // consider whether to have the collision make a sound const float AUDIBLE_COLLISION_THRESHOLD = 0.02f; - const float COLLISION_LOUDNESS = 1.f; + const float COLLISION_LOUDNESS = 1.0f; const float DURATION_SCALING = 0.004f; const float NOISE_SCALING = 0.1f; glm::vec3 velocity = _velocity; @@ -826,10 +851,10 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim // Noise is a function of the angle of collision // Duration of the sound is a function of both base frequency and velocity of impact Application::getInstance()->getAudio()->startCollisionSound( - std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.f), - frequency * (1.f + velocityTangentToCollision / velocityTowardCollision), - std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.f), - 1.f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true); + std::min(COLLISION_LOUDNESS * velocityTowardCollision, 1.0f), + frequency * (1.0f + velocityTangentToCollision / velocityTowardCollision), + std::min(velocityTangentToCollision / velocityTowardCollision * NOISE_SCALING, 1.0f), + 1.0f - DURATION_SCALING * powf(frequency, 0.5f) / velocityTowardCollision, true); } } @@ -842,8 +867,8 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float float halfHeights = 0.5 * (heightA + heightB); if (yDistance < halfHeights) { // cylinders collide - if (xzDistance > 0.f) { - positionBA.y = 0.f; + if (xzDistance > 0.0f) { + positionBA.y = 0.0f; // note, penetration should point from A into B penetration = positionBA * ((radiusA + radiusB - xzDistance) / xzDistance); return true; @@ -853,7 +878,7 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float } } else if (yDistance < halfHeights + radiusA + radiusB) { // caps collide - if (positionBA.y < 0.f) { + if (positionBA.y < 0.0f) { // A is above B positionBA.y += halfHeights; float BA = glm::length(positionBA); @@ -1068,15 +1093,15 @@ void MyAvatar::goHome() { } void MyAvatar::increaseSize() { - if ((1.f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { - _targetScale *= (1.f + SCALING_RATIO); + if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { + _targetScale *= (1.0f + SCALING_RATIO); qDebug("Changed scale to %f", _targetScale); } } void MyAvatar::decreaseSize() { - if (MIN_AVATAR_SCALE < (1.f - SCALING_RATIO) * _targetScale) { - _targetScale *= (1.f - SCALING_RATIO); + if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) { + _targetScale *= (1.0f - SCALING_RATIO); qDebug("Changed scale to %f", _targetScale); } } @@ -1152,8 +1177,8 @@ void MyAvatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& pe if (leverLength > EPSILON) { // compute lean perturbation angles glm::quat bodyRotation = getOrientation(); - glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); - glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); + glm::vec3 xAxis = bodyRotation * glm::vec3(1.0f, 0.0f, 0.0f); + glm::vec3 zAxis = bodyRotation * glm::vec3(0.0f, 0.0f, 1.0f); leverAxis = leverAxis / leverLength; glm::vec3 effectivePenetration = penetration - glm::dot(penetration, leverAxis) * leverAxis; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 946481f3e6..ec33847255 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -57,7 +57,6 @@ public: float getLeanScale() const { return _leanScale; } float getElapsedTimeStopped() const { return _elapsedTimeStopped; } float getElapsedTimeMoving() const { return _elapsedTimeMoving; } - float getAbsoluteHeadYaw() const; // degrees const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } glm::vec3 getGravity() const { return _gravity; } @@ -128,8 +127,8 @@ private: QWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; bool _shouldRender; - bool _billboardValid; + float _oculusYawOffset; // private methods void updateThrust(float deltaTime); diff --git a/interface/src/devices/FaceTracker.cpp b/interface/src/devices/FaceTracker.cpp new file mode 100644 index 0000000000..52fe04de77 --- /dev/null +++ b/interface/src/devices/FaceTracker.cpp @@ -0,0 +1,17 @@ +// +// FaceTracker.cpp +// interface/src/devices +// +// Created by Andrzej Kapolka on 4/9/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "FaceTracker.h" + +FaceTracker::FaceTracker() : + _estimatedEyePitch(0.0f), + _estimatedEyeYaw(0.0f) { +} diff --git a/interface/src/devices/FaceTracker.h b/interface/src/devices/FaceTracker.h new file mode 100644 index 0000000000..459f38cafc --- /dev/null +++ b/interface/src/devices/FaceTracker.h @@ -0,0 +1,46 @@ +// +// FaceTracker.h +// interface/src/devices +// +// Created by Andrzej Kapolka on 4/9/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_FaceTracker_h +#define hifi_FaceTracker_h + +#include +#include + +#include +#include + +/// Base class for face trackers (Faceshift, Visage, Faceplus). +class FaceTracker : public QObject { + Q_OBJECT + +public: + + FaceTracker(); + + const glm::vec3& getHeadTranslation() const { return _headTranslation; } + const glm::quat& getHeadRotation() const { return _headRotation; } + + float getEstimatedEyePitch() const { return _estimatedEyePitch; } + float getEstimatedEyeYaw() const { return _estimatedEyeYaw; } + + const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } + +protected: + + glm::vec3 _headTranslation; + glm::quat _headRotation; + float _estimatedEyePitch; + float _estimatedEyeYaw; + QVector _blendshapeCoefficients; +}; + +#endif // hifi_FaceTracker_h diff --git a/interface/src/devices/Faceplus.cpp b/interface/src/devices/Faceplus.cpp new file mode 100644 index 0000000000..f7f2f1f1bd --- /dev/null +++ b/interface/src/devices/Faceplus.cpp @@ -0,0 +1,230 @@ +// +// Faceplus.cpp +// interface/src/devices +// +// Created by Andrzej Kapolka on 4/9/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#ifdef HAVE_FACEPLUS +#include +#endif + +#include "Application.h" +#include "Faceplus.h" +#include "renderer/FBXReader.h" + +static int floatVectorMetaTypeId = qRegisterMetaType >(); + +Faceplus::Faceplus() : + _enabled(false), + _active(false) { + +#ifdef HAVE_FACEPLUS + // these are ignored--any values will do + faceplus_log_in("username", "password"); +#endif +} + +Faceplus::~Faceplus() { + setEnabled(false); +} + +void Faceplus::init() { + connect(Application::getInstance()->getFaceshift(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled())); + updateEnabled(); +} + +void Faceplus::setState(const glm::quat& headRotation, float estimatedEyePitch, float estimatedEyeYaw, + const QVector& blendshapeCoefficients) { + _headRotation = headRotation; + _estimatedEyePitch = estimatedEyePitch; + _estimatedEyeYaw = estimatedEyeYaw; + _blendshapeCoefficients = blendshapeCoefficients; + _active = true; +} + +void Faceplus::updateEnabled() { + setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) && + !(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && + Application::getInstance()->getFaceshift()->isConnectedOrConnecting())); +} + +void Faceplus::setEnabled(bool enabled) { + if (_enabled == enabled) { + return; + } + if ((_enabled = enabled)) { + _reader = new FaceplusReader(); + QThread* readerThread = new QThread(this); + _reader->moveToThread(readerThread); + readerThread->start(); + QMetaObject::invokeMethod(_reader, "init"); + + } else { + QThread* readerThread = _reader->thread(); + QMetaObject::invokeMethod(_reader, "shutdown"); + readerThread->wait(); + delete readerThread; + _active = false; + } +} + +#ifdef HAVE_FACEPLUS +static QMultiHash > createChannelNameMap() { + QMultiHash > blendshapeMap; + blendshapeMap.insert("EyeBlink_L", QPair("Mix::Blink_Left", 1.0f)); + blendshapeMap.insert("EyeBlink_R", QPair("Mix::Blink_Right", 1.0f)); + blendshapeMap.insert("BrowsD_L", QPair("Mix::BrowsDown_Left", 1.0f)); + blendshapeMap.insert("BrowsD_R", QPair("Mix::BrowsDown_Right", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::BrowsIn_Left", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::BrowsIn_Right", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::BrowsOuterLower_Left", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::BrowsOuterLower_Right", 1.0f)); + blendshapeMap.insert("BrowsU_L", QPair("Mix::BrowsUp_Left", 10.0f)); + blendshapeMap.insert("BrowsU_R", QPair("Mix::BrowsUp_Right", 10.0f)); + blendshapeMap.insert("EyeOpen_L", QPair("Mix::EyesWide_Left", 1.0f)); + blendshapeMap.insert("EyeOpen_R", QPair("Mix::EyesWide_Right", 1.0f)); + blendshapeMap.insert("MouthFrown_L", QPair("Mix::Frown_Left", 1.0f)); + blendshapeMap.insert("MouthFrown_R", QPair("Mix::Frown_Right", 1.0f)); + blendshapeMap.insert("JawLeft", QPair("Mix::Jaw_RotateY_Left", 1.0f)); + blendshapeMap.insert("JawRight", QPair("Mix::Jaw_RotateY_Right", 1.0f)); + blendshapeMap.insert("LipsLowerDown", QPair("Mix::LowerLipDown_Left", 0.5f)); + blendshapeMap.insert("LipsLowerDown", QPair("Mix::LowerLipDown_Right", 0.5f)); + blendshapeMap.insert("...", QPair("Mix::LowerLipIn", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::LowerLipOut", 1.0f)); + blendshapeMap.insert("MouthLeft", QPair("Mix::Midmouth_Left", 1.0f)); + blendshapeMap.insert("MouthRight", QPair("Mix::Midmouth_Right", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::MouthDown", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::MouthNarrow_Left", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::MouthNarrow_Right", 1.0f)); + blendshapeMap.insert("JawOpen", QPair("Mix::MouthOpen", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::MouthUp", 1.0f)); + blendshapeMap.insert("LipsPucker", QPair("Mix::MouthWhistle_NarrowAdjust_Left", 0.5f)); + blendshapeMap.insert("LipsPucker", QPair("Mix::MouthWhistle_NarrowAdjust_Right", 0.5f)); + blendshapeMap.insert("Sneer", QPair("Mix::NoseScrunch_Left", 0.5f)); + blendshapeMap.insert("Sneer", QPair("Mix::NoseScrunch_Right", 0.5f)); + blendshapeMap.insert("MouthSmile_L", QPair("Mix::Smile_Left", 1.0f)); + blendshapeMap.insert("MouthSmile_R", QPair("Mix::Smile_Right", 1.0f)); + blendshapeMap.insert("EyeSquint_L", QPair("Mix::Squint_Left", 1.0f)); + blendshapeMap.insert("EyeSquint_R", QPair("Mix::Squint_Right", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::UpperLipIn", 1.0f)); + blendshapeMap.insert("...", QPair("Mix::UpperLipOut", 1.0f)); + blendshapeMap.insert("LipsUpperUp", QPair("Mix::UpperLipUp_Left", 0.5f)); + blendshapeMap.insert("LipsUpperUp", QPair("Mix::UpperLipUp_Right", 0.5f)); + + QMultiHash > channelNameMap; + for (int i = 0;; i++) { + QByteArray blendshape = FACESHIFT_BLENDSHAPES[i]; + if (blendshape.isEmpty()) { + break; + } + for (QMultiHash >::const_iterator it = blendshapeMap.constFind(blendshape); + it != blendshapeMap.constEnd() && it.key() == blendshape; it++) { + channelNameMap.insert(it.value().first, QPair(i, it.value().second)); + } + } + + return channelNameMap; +} + +static const QMultiHash >& getChannelNameMap() { + static QMultiHash > channelNameMap = createChannelNameMap(); + return channelNameMap; +} +#endif + +FaceplusReader::~FaceplusReader() { +#ifdef HAVE_FACEPLUS + if (faceplus_teardown()) { + qDebug() << "Faceplus torn down."; + } +#endif +} + +void FaceplusReader::init() { +#ifdef HAVE_FACEPLUS + if (!faceplus_init("VGA")) { + qDebug() << "Failed to initialized Faceplus."; + return; + } + qDebug() << "Faceplus initialized."; + + int channelCount = faceplus_output_channels_count(); + _outputVector.resize(channelCount); + + int maxIndex = -1; + _channelIndexMap.clear(); + for (int i = 0; i < channelCount; i++) { + QByteArray name = faceplus_output_channel_name(i); + if (name == "Head_Joint::Rotation_X") { + _headRotationIndices[0] = i; + + } else if (name == "Head_Joint::Rotation_Y") { + _headRotationIndices[1] = i; + + } else if (name == "Head_Joint::Rotation_Z") { + _headRotationIndices[2] = i; + + } else if (name == "Left_Eye_Joint::Rotation_X") { + _leftEyeRotationIndices[0] = i; + + } else if (name == "Left_Eye_Joint::Rotation_Y") { + _leftEyeRotationIndices[1] = i; + + } else if (name == "Right_Eye_Joint::Rotation_X") { + _rightEyeRotationIndices[0] = i; + + } else if (name == "Right_Eye_Joint::Rotation_Y") { + _rightEyeRotationIndices[1] = i; + } + for (QMultiHash >::const_iterator it = getChannelNameMap().constFind(name); + it != getChannelNameMap().constEnd() && it.key() == name; it++) { + _channelIndexMap.insert(i, it.value()); + maxIndex = qMax(maxIndex, it.value().first); + } + } + _blendshapeCoefficients.resize(maxIndex + 1); + + QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); +#endif +} + +void FaceplusReader::shutdown() { + deleteLater(); + thread()->quit(); +} + +void FaceplusReader::update() { +#ifdef HAVE_FACEPLUS + if (!(faceplus_synchronous_track() && faceplus_current_output_vector(_outputVector.data()))) { + QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); + return; + } + glm::quat headRotation(glm::radians(glm::vec3(-_outputVector.at(_headRotationIndices[0]), + _outputVector.at(_headRotationIndices[1]), -_outputVector.at(_headRotationIndices[2])))); + float estimatedEyePitch = (_outputVector.at(_leftEyeRotationIndices[0]) + + _outputVector.at(_rightEyeRotationIndices[0])) * -0.5f; + float estimatedEyeYaw = (_outputVector.at(_leftEyeRotationIndices[1]) + + _outputVector.at(_rightEyeRotationIndices[1])) * 0.5f; + + qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f); + for (int i = 0; i < _outputVector.size(); i++) { + for (QMultiHash >::const_iterator it = _channelIndexMap.constFind(i); + it != _channelIndexMap.constEnd() && it.key() == i; it++) { + _blendshapeCoefficients[it.value().first] += _outputVector.at(i) * it.value().second; + } + } + + QMetaObject::invokeMethod(Application::getInstance()->getFaceplus(), "setState", Q_ARG(const glm::quat&, headRotation), + Q_ARG(float, estimatedEyePitch), Q_ARG(float, estimatedEyeYaw), Q_ARG(const QVector&, _blendshapeCoefficients)); + + QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); +#endif +} + diff --git a/interface/src/devices/Faceplus.h b/interface/src/devices/Faceplus.h new file mode 100644 index 0000000000..2b9219f3fd --- /dev/null +++ b/interface/src/devices/Faceplus.h @@ -0,0 +1,79 @@ +// +// Faceplus.h +// interface/src/devices +// +// Created by Andrzej Kapolka on 4/9/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Faceplus_h +#define hifi_Faceplus_h + +#include +#include +#include + +#include "FaceTracker.h" + +class FaceplusReader; + +/// Interface for Mixamo FacePlus. +class Faceplus : public FaceTracker { + Q_OBJECT + +public: + + Faceplus(); + virtual ~Faceplus(); + + void init(); + + bool isActive() const { return _active; } + + Q_INVOKABLE void setState(const glm::quat& headRotation, float estimatedEyePitch, float estimatedEyeYaw, + const QVector& blendshapeCoefficients); + +public slots: + + void updateEnabled(); + +private: + + void setEnabled(bool enabled); + + bool _enabled; + bool _active; + + FaceplusReader* _reader; +}; + +Q_DECLARE_METATYPE(QVector) + +/// The reader object that lives in its own thread. +class FaceplusReader : public QObject { + Q_OBJECT + +public: + + virtual ~FaceplusReader(); + + Q_INVOKABLE void init(); + Q_INVOKABLE void shutdown(); + Q_INVOKABLE void update(); + +private: + +#ifdef HAVE_FACEPLUS + QMultiHash > _channelIndexMap; + QVector _outputVector; + int _headRotationIndices[3]; + int _leftEyeRotationIndices[2]; + int _rightEyeRotationIndices[2]; + QVector _blendshapeCoefficients; +#endif +}; + +#endif // hifi_Faceplus_h diff --git a/interface/src/devices/Faceshift.cpp b/interface/src/devices/Faceshift.cpp index 1fd4b4bf1d..59f2c245df 100644 --- a/interface/src/devices/Faceshift.cpp +++ b/interface/src/devices/Faceshift.cpp @@ -45,9 +45,7 @@ Faceshift::Faceshift() : _jawOpenIndex(21), _longTermAverageEyePitch(0.0f), _longTermAverageEyeYaw(0.0f), - _longTermAverageInitialized(false), - _estimatedEyePitch(0.0f), - _estimatedEyeYaw(0.0f) + _longTermAverageInitialized(false) { connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected())); connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError))); diff --git a/interface/src/devices/Faceshift.h b/interface/src/devices/Faceshift.h index 157409882c..2fc1aaddb1 100644 --- a/interface/src/devices/Faceshift.h +++ b/interface/src/devices/Faceshift.h @@ -15,13 +15,12 @@ #include #include -#include -#include - #include +#include "FaceTracker.h" + /// Handles interaction with the Faceshift software, which provides head position/orientation and facial features. -class Faceshift : public QObject { +class Faceshift : public FaceTracker { Q_OBJECT public: @@ -34,9 +33,7 @@ public: bool isActive() const; - const glm::quat& getHeadRotation() const { return _headRotation; } const glm::vec3& getHeadAngularVelocity() const { return _headAngularVelocity; } - const glm::vec3& getHeadTranslation() const { return _headTranslation; } // these pitch/yaw angles are in degrees float getEyeGazeLeftPitch() const { return _eyeGazeLeftPitch; } @@ -45,11 +42,6 @@ public: float getEyeGazeRightPitch() const { return _eyeGazeRightPitch; } float getEyeGazeRightYaw() const { return _eyeGazeRightYaw; } - float getEstimatedEyePitch() const { return _estimatedEyePitch; } - float getEstimatedEyeYaw() const { return _estimatedEyeYaw; } - - const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } - float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); } float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); } float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); } @@ -102,9 +94,7 @@ private: bool _tracking; quint64 _lastTrackingStateReceived; - glm::quat _headRotation; glm::vec3 _headAngularVelocity; - glm::vec3 _headTranslation; // degrees float _eyeGazeLeftPitch; @@ -112,8 +102,6 @@ private: float _eyeGazeRightPitch; float _eyeGazeRightYaw; - QVector _blendshapeCoefficients; - int _leftBlinkIndex; int _rightBlinkIndex; int _leftEyeOpenIndex; @@ -135,10 +123,6 @@ private: float _longTermAverageEyePitch; float _longTermAverageEyeYaw; bool _longTermAverageInitialized; - - // degrees - float _estimatedEyePitch; - float _estimatedEyeYaw; }; #endif // hifi_Faceshift_h diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index a69fd48087..d3da8fe7c3 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -30,7 +30,6 @@ int OculusManager::_scaleLocation; int OculusManager::_scaleInLocation; int OculusManager::_hmdWarpParamLocation; bool OculusManager::_isConnected = false; -float OculusManager::_yawOffset = 0.0f; // radians #ifdef HAVE_LIBOVR using namespace OVR; @@ -190,18 +189,9 @@ void OculusManager::reset() { #endif } -void OculusManager::updateYawOffset() { -#ifdef HAVE_LIBOVR - float yaw, pitch, roll; - _sensorFusion->GetOrientation().GetEulerAngles(&yaw, &pitch, &roll); - _yawOffset = yaw; -#endif -} - void OculusManager::getEulerAngles(float& yaw, float& pitch, float& roll) { #ifdef HAVE_LIBOVR _sensorFusion->GetOrientation().GetEulerAngles(&yaw, &pitch, &roll); - yaw = yaw - _yawOffset; #endif } diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 09e204d795..6376df05ca 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -51,7 +51,6 @@ private: static int _scaleInLocation; static int _hmdWarpParamLocation; static bool _isConnected; - static float _yawOffset; #ifdef HAVE_LIBOVR static OVR::Ptr _deviceManager; diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 72f0306e79..e2f40e4741 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -37,9 +37,7 @@ const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f); Visage::Visage() : _enabled(false), _active(false), - _headOrigin(DEFAULT_HEAD_ORIGIN), - _estimatedEyePitch(0.0f), - _estimatedEyeYaw(0.0f) { + _headOrigin(DEFAULT_HEAD_ORIGIN) { #ifdef HAVE_VISAGE QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc"; @@ -164,6 +162,7 @@ void Visage::reset() { void Visage::updateEnabled() { setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) && + !Menu::getInstance()->isOptionChecked(MenuOption::Faceplus) && !(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && Application::getInstance()->getFaceshift()->isConnectedOrConnecting())); } diff --git a/interface/src/devices/Visage.h b/interface/src/devices/Visage.h index 68c5055954..33e2f0a7e1 100644 --- a/interface/src/devices/Visage.h +++ b/interface/src/devices/Visage.h @@ -16,8 +16,7 @@ #include #include -#include -#include +#include "FaceTracker.h" namespace VisageSDK { class VisageTracker2; @@ -25,7 +24,7 @@ namespace VisageSDK { } /// Handles input from the Visage webcam feature tracking software. -class Visage : public QObject { +class Visage : public FaceTracker { Q_OBJECT public: @@ -37,14 +36,6 @@ public: bool isActive() const { return _active; } - const glm::quat& getHeadRotation() const { return _headRotation; } - const glm::vec3& getHeadTranslation() const { return _headTranslation; } - - float getEstimatedEyePitch() const { return _estimatedEyePitch; } - float getEstimatedEyeYaw() const { return _estimatedEyeYaw; } - - const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } - void update(); void reset(); @@ -64,15 +55,8 @@ private: bool _enabled; bool _active; - glm::quat _headRotation; - glm::vec3 _headTranslation; - + glm::vec3 _headOrigin; - - float _estimatedEyePitch; - float _estimatedEyeYaw; - - QVector _blendshapeCoefficients; }; #endif // hifi_Visage_h diff --git a/interface/src/ui/Oscilloscope.cpp b/interface/src/ui/Oscilloscope.cpp deleted file mode 100644 index af10539c43..0000000000 --- a/interface/src/ui/Oscilloscope.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// -// Oscilloscope.cpp -// interface/src/ui -// -// Created by Philip on 1/28/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include -#include - -#include "InterfaceConfig.h" - -#include "Oscilloscope.h" - -// Reimplemented 4/26/13 (tosh) - don't blame Philip for bugs - -using namespace std; - -namespace { // everything in here only exists while compiling this .cpp file - - // one sample buffer per channel - unsigned const MAX_SAMPLES = Oscilloscope::MAX_SAMPLES_PER_CHANNEL * Oscilloscope::MAX_CHANNELS; - - // adding an x-coordinate yields twice the amount of vertices - unsigned const MAX_COORDS_PER_CHANNEL = Oscilloscope::MAX_SAMPLES_PER_CHANNEL * 2; - // allocated once for each channel - unsigned const MAX_COORDS = MAX_COORDS_PER_CHANNEL * Oscilloscope::MAX_CHANNELS; - - // total amount of memory to allocate (in 16-bit integers) - unsigned const N_INT16_TO_ALLOC = MAX_SAMPLES + MAX_COORDS; -} - - -Oscilloscope::Oscilloscope(int w, int h, bool isEnabled) : - enabled(isEnabled), - inputPaused(false), - _width(w), - _height(h), - _samples(0l), - _vertices(0l), - // some filtering (see details in Log.h) - _lowPassCoeff(0.4f), - // three in -> one out - _downsampleRatio(3) { - - // allocate enough space for the sample data and to turn it into - // vertices and since they're all 'short', do so in one shot - _samples = new short[N_INT16_TO_ALLOC]; - memset(_samples, 0, N_INT16_TO_ALLOC * sizeof(short)); - _vertices = _samples + MAX_SAMPLES; - - // initialize write positions to start of each channel's region - for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch) { - _writePos[ch] = MAX_SAMPLES_PER_CHANNEL * ch; - } - - _colors[0] = 0xffffff; - _colors[1] = 0x00ffff; - _colors[2] = 0x00ffff; -} - -Oscilloscope::~Oscilloscope() { - - delete[] _samples; -} - -void Oscilloscope::addSamples(const QByteArray& audioByteArray, bool isStereo, bool isInput) { - - if (! enabled || inputPaused) { - return; - } - - unsigned numSamplesPerChannel = audioByteArray.size() / (sizeof(int16_t) * (isStereo ? 2 : 1)); - int16_t* samples = (int16_t*) audioByteArray.data(); - - for (int channel = 0; channel < (isStereo ? 2 : 1); channel++) { - // add samples for each of the channels - - // determine start/end offset of this channel's region - unsigned baseOffs = MAX_SAMPLES_PER_CHANNEL * (channel + !isInput); - unsigned endOffs = baseOffs + MAX_SAMPLES_PER_CHANNEL; - - // fetch write position for this channel - unsigned writePos = _writePos[channel + !isInput]; - - // determine write position after adding the samples - unsigned newWritePos = writePos + numSamplesPerChannel; - unsigned n2 = 0; - if (newWritePos >= endOffs) { - // passed boundary of the circular buffer? -> we need to copy two blocks - n2 = newWritePos - endOffs; - newWritePos = baseOffs + n2; - numSamplesPerChannel -= n2; - } - - if (!isStereo) { - // copy data - memcpy(_samples + writePos, samples, numSamplesPerChannel * sizeof(int16_t)); - if (n2 > 0) { - memcpy(_samples + baseOffs, samples + numSamplesPerChannel, n2 * sizeof(int16_t)); - } - } else { - // we have interleaved samples we need to separate into two channels - for (unsigned i = 0; i < numSamplesPerChannel + n2; i++) { - if (i < numSamplesPerChannel - n2) { - _samples[writePos] = samples[(i * 2) + channel]; - } else { - _samples[baseOffs] = samples[(i * 2) + channel]; - } - } - } - - // set new write position for this channel - _writePos[channel + !isInput] = newWritePos; - } -} - -void Oscilloscope::render(int x, int y) { - - if (! enabled) { - return; - } - - // fetch low pass factor (and convert to fix point) / downsample factor - int lowPassFixPt = -(int)(std::numeric_limits::min()) * _lowPassCoeff; - unsigned downsample = _downsampleRatio; - // keep half of the buffer for writing and ensure an even vertex count - unsigned usedWidth = min(_width, MAX_SAMPLES_PER_CHANNEL / (downsample * 2)) & ~1u; - unsigned usedSamples = usedWidth * downsample; - - // expand samples to vertex data - for (unsigned ch = 0; ch < MAX_CHANNELS; ++ch) { - // for each channel: determine memory regions - short const* basePtr = _samples + MAX_SAMPLES_PER_CHANNEL * ch; - short const* endPtr = basePtr + MAX_SAMPLES_PER_CHANNEL; - short const* inPtr = _samples + _writePos[ch]; - short* outPtr = _vertices + MAX_COORDS_PER_CHANNEL * ch; - int sample = 0, x = usedWidth; - for (int i = (int)usedSamples; --i >= 0 ;) { - if (inPtr == basePtr) { - // handle boundary, reading the circular sample buffer - inPtr = endPtr; - } - // read and (eventually) filter sample - sample += ((*--inPtr - sample) * lowPassFixPt) >> 15; - // write every nth as y with a corresponding x-coordinate - if (i % downsample == 0) { - *outPtr++ = short(--x); - *outPtr++ = short(sample); - } - } - } - - // set up rendering state (vertex data lives at _vertices) - glLineWidth(1.0); - glDisable(GL_LINE_SMOOTH); - glPushMatrix(); - glTranslatef((float)x + 0.0f, (float)y + _height / 2.0f, 0.0f); - glScaled(1.0f, _height / 32767.0f, 1.0f); - glVertexPointer(2, GL_SHORT, 0, _vertices); - glEnableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_INDEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - - // render channel 0 - glColor3ub(GLubyte(_colors[0] >> 16), GLubyte((_colors[0] >> 8) & 0xff), GLubyte(_colors[0] & 0xff)); - glDrawArrays(GL_LINES, MAX_SAMPLES_PER_CHANNEL * 0, usedWidth); - - // render channel 1 - glColor3f(0.0f, 1.0f ,1.0f); - glColor3ub(GLubyte(_colors[1] >> 16), GLubyte((_colors[1] >> 8) & 0xff), GLubyte(_colors[1] & 0xff)); - glDrawArrays(GL_LINES, MAX_SAMPLES_PER_CHANNEL * 1, usedWidth); - - // render channel 2 - glColor3ub(GLubyte(_colors[2] >> 16), GLubyte((_colors[2] >> 8) & 0xff), GLubyte(_colors[2] & 0xff)); - glDrawArrays(GL_LINES, MAX_SAMPLES_PER_CHANNEL * 2, usedWidth); - - // reset rendering state - glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); -} - diff --git a/interface/src/ui/Oscilloscope.h b/interface/src/ui/Oscilloscope.h deleted file mode 100644 index 6eff547530..0000000000 --- a/interface/src/ui/Oscilloscope.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// Oscilloscope.h -// interface/src/ui -// -// Created by Philip on 1/28/13. -// Copyright 2013 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_Oscilloscope_h -#define hifi_Oscilloscope_h - -#include - -#include - -class Oscilloscope : public QObject { - Q_OBJECT -public: - Oscilloscope(int width, int height, bool isEnabled); - ~Oscilloscope(); - - void render(int x, int y); - - // Switches: On/Off, Stop Time - volatile bool enabled; - volatile bool inputPaused; - - // Limits - static unsigned const MAX_CHANNELS = 3; - static unsigned const MAX_SAMPLES_PER_CHANNEL = 4096; - - // Sets the color for a specific channel. - void setColor(unsigned ch, unsigned rgb) { assert(ch < MAX_CHANNELS); if (! inputPaused) { _colors[ch] = rgb; } } - - // Controls a simple one pole IIR low pass filter that is provided to - // reduce high frequencies aliasing (to lower ones) when downsampling. - // - // The parameter sets the influence of the input in respect to the - // feed-back signal on the output. - // - // +---------+ - // in O--------------|+ ideal |--o--------------O out - // .---|- op amp | | - // | +---------+ | - // | | - // o-------||-------o - // | | - // | __V__ - // -------------|_____|-------+ - // : : | - // 0.0 - 1.0 (GND) - // - // The values in range 0.0 - 1.0 correspond to "all closed" (input has - // no influence on the output) to "all open" (feedback has no influence - // on the output) configurations. - void setLowpassOpenness(float w) { assert(w >= 0.0f && w <= 1.0f); _lowPassCoeff = w; } - - // Sets the number of input samples per output sample. Without filtering - // just uses every nTh sample. - void setDownsampleRatio(unsigned n) { assert(n > 0); _downsampleRatio = n; } -public slots: - void addSamples(const QByteArray& audioByteArray, bool isStereo, bool isInput); -private: - // don't copy/assign - Oscilloscope(Oscilloscope const&); // = delete; - Oscilloscope& operator=(Oscilloscope const&); // = delete; - - // state variables - - unsigned _width; - unsigned _height; - short* _samples; - short* _vertices; - unsigned _writePos[MAX_CHANNELS]; - - float _lowPassCoeff; - unsigned _downsampleRatio; - unsigned _colors[MAX_CHANNELS]; -}; - -#endif // hifi_Oscilloscope_h