// // Visage.cpp // interface/src/devices // // Created by Andrzej Kapolka on 2/11/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 #include #include #include #include #include #include "Application.h" #include "Faceshift.h" #include "Visage.h" // this has to go after our normal includes, because its definition of HANDLE conflicts with Qt's #ifdef HAVE_VISAGE #include #endif namespace VisageSDK { #ifdef WIN32 void __declspec(dllimport) initializeLicenseManager(char* licenseKeyFileName); #else void initializeLicenseManager(char* licenseKeyFileName); #endif } using namespace VisageSDK; const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f); Visage::Visage() : _enabled(false), _headOrigin(DEFAULT_HEAD_ORIGIN) { #ifdef HAVE_VISAGE #ifdef WIN32 QByteArray licensePath = PathUtils::resourcesPath().toLatin1() + "visage"; #else QByteArray licensePath = PathUtils::resourcesPath().toLatin1() + "visage/license.vlc"; #endif initializeLicenseManager(licensePath.data()); _tracker = new VisageTracker2(PathUtils::resourcesPath().toLatin1() + "visage/tracker.cfg"); _data = new FaceData(); #endif } Visage::~Visage() { #ifdef HAVE_VISAGE _tracker->stop(); // deleting the tracker crashes windows; disable for now //delete _tracker; delete _data; #endif } #ifdef HAVE_VISAGE static int leftEyeBlinkIndex = 0; static int rightEyeBlinkIndex = 1; static QMultiHash > createActionUnitNameMap() { QMultiHash > blendshapeMap; blendshapeMap.insert("JawFwd", QPair("au_jaw_z_push", 1.0f)); blendshapeMap.insert("JawLeft", QPair("au_jaw_x_push", 1.0f)); blendshapeMap.insert("JawOpen", QPair("au_jaw_drop", 1.0f)); blendshapeMap.insert("LipsLowerDown", QPair("au_lower_lip_drop", 1.0f)); blendshapeMap.insert("LipsUpperOpen", QPair("au_upper_lip_raiser", 1.0f)); blendshapeMap.insert("LipsStretch_R", QPair("au_lip_stretcher_left", 0.5f)); blendshapeMap.insert("MouthSmile_L", QPair("au_lip_corner_depressor", -1.0f)); blendshapeMap.insert("MouthSmile_R", QPair("au_lip_corner_depressor", -1.0f)); blendshapeMap.insert("BrowsU_R", QPair("au_left_outer_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsU_C", QPair("au_left_inner_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsD_R", QPair("au_left_brow_lowerer", 1.0f)); blendshapeMap.insert("EyeBlink_L", QPair("au_leye_closed", 1.0f)); blendshapeMap.insert("EyeBlink_R", QPair("au_reye_closed", 1.0f)); blendshapeMap.insert("EyeOpen_L", QPair("au_upper_lid_raiser", 1.0f)); blendshapeMap.insert("EyeOpen_R", QPair("au_upper_lid_raiser", 1.0f)); blendshapeMap.insert("LipLowerOpen", QPair("au_lower_lip_x_push", 1.0f)); blendshapeMap.insert("LipsStretch_L", QPair("au_lip_stretcher_right", 0.5f)); blendshapeMap.insert("BrowsU_L", QPair("au_right_outer_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsU_C", QPair("au_right_inner_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsD_L", QPair("au_right_brow_lowerer", 1.0f)); QMultiHash > actionUnitNameMap; for (int i = 0;; i++) { QByteArray blendshape = FACESHIFT_BLENDSHAPES[i]; if (blendshape.isEmpty()) { break; } if (blendshape == "EyeBlink_L") { leftEyeBlinkIndex = i; } else if (blendshape == "EyeBlink_R") { rightEyeBlinkIndex = i; } for (QMultiHash >::const_iterator it = blendshapeMap.constFind(blendshape); it != blendshapeMap.constEnd() && it.key() == blendshape; it++) { actionUnitNameMap.insert(it.value().first, QPair(i, it.value().second)); } } return actionUnitNameMap; } static const QMultiHash >& getActionUnitNameMap() { static QMultiHash > actionUnitNameMap = createActionUnitNameMap(); return actionUnitNameMap; } #endif #ifdef HAVE_VISAGE const float TRANSLATION_SCALE = 20.0f; void Visage::init() { connect(DependencyManager::get().data(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled())); updateEnabled(); } void Visage::update(float deltaTime) { if (!isActive()) { return; } FaceTracker::update(deltaTime); _headRotation = glm::quat(glm::vec3(-_data->faceRotation[0], -_data->faceRotation[1], _data->faceRotation[2])); _headTranslation = (glm::vec3(_data->faceTranslation[0], _data->faceTranslation[1], _data->faceTranslation[2]) - _headOrigin) * TRANSLATION_SCALE; _estimatedEyePitch = glm::degrees(-_data->gazeDirection[1]); _estimatedEyeYaw = glm::degrees(-_data->gazeDirection[0]); if (_actionUnitIndexMap.isEmpty()) { int maxIndex = -1; for (int i = 0; i < _data->actionUnitCount; i++) { QByteArray name = _data->actionUnitsNames[i]; for (QMultiHash >::const_iterator it = getActionUnitNameMap().constFind(name); it != getActionUnitNameMap().constEnd() && it.key() == name; it++) { _actionUnitIndexMap.insert(i, it.value()); maxIndex = qMax(maxIndex, it.value().first); } } _blendshapeCoefficients.resize(maxIndex + 1); } qFill(_blendshapeCoefficients.begin(), _blendshapeCoefficients.end(), 0.0f); for (int i = 0; i < _data->actionUnitCount; i++) { if (!_data->actionUnitsUsed[i]) { continue; } for (QMultiHash >::const_iterator it = _actionUnitIndexMap.constFind(i); it != _actionUnitIndexMap.constEnd() && it.key() == i; it++) { _blendshapeCoefficients[it.value().first] += _data->actionUnits[i] * it.value().second; } } _blendshapeCoefficients[leftEyeBlinkIndex] = 1.0f - _data->eyeClosure[1]; _blendshapeCoefficients[rightEyeBlinkIndex] = 1.0f - _data->eyeClosure[0]; } void Visage::reset() { _headOrigin += _headTranslation / TRANSLATION_SCALE; } #endif void Visage::updateEnabled() { setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) && !(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) && DependencyManager::get()->isConnectedOrConnecting()) && !Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression)); } void Visage::setEnabled(bool enabled) { #ifdef HAVE_VISAGE if (_enabled == enabled) { return; } if ((_enabled = enabled)) { _tracker->trackFromCam(); } else { _tracker->stop(); } #endif }