// // Visage.cpp // interface // // Created by Andrzej Kapolka on 2/11/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // #include #include #ifdef HAVE_VISAGE #include #endif #include "Visage.h" #include "renderer/FBXReader.h" 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() : _active(false), _headOrigin(DEFAULT_HEAD_ORIGIN), _estimatedEyePitch(0.0f), _estimatedEyeYaw(0.0f) { #ifdef HAVE_VISAGE QByteArray licensePath = ":/visage/license.vlc"; initializeLicenseManager(licensePath.data()); _tracker = new VisageTracker2(":/visage/Facial Features Tracker - Asymmetric.cfg"); if (_tracker->trackFromCam()) { _data = new FaceData(); } else { delete _tracker; _tracker = NULL; } #endif } Visage::~Visage() { #ifdef HAVE_VISAGE if (_tracker) { _tracker->stop(); delete _tracker; delete _data; } #endif } #ifdef HAVE_VISAGE static int leftEyeBlinkIndex = 0; static int rightEyeBlinkIndex = 1; static QMultiHash > createActionUnitNameMap() { QMultiHash > blendshapeMap; blendshapeMap.insert("Sneer", QPair("au_nose_wrinkler", 1.0f)); 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("LipsUpperUp", QPair("au_upper_lip_raiser", 1.0f)); blendshapeMap.insert("LipsStretch_R", QPair("au_lip_stretcher_left", 1.0f)); blendshapeMap.insert("MouthSmile_L", QPair("au_lip_corner_depressor", -1.0f)); blendshapeMap.insert("MouthSmile_R", QPair("au_lip_corner_depressor", -1.0f)); blendshapeMap.insert("LipsPucker", QPair("au_lip_presser", 0.0f)); blendshapeMap.insert("BrowsU_R", QPair("au_left_outer_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsU_C", QPair("au_left_inner_brow_raiser", 0.5f)); 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_leye_closed", 1.0f)); blendshapeMap.insert("EyeSquint_L", QPair("au_lid_tightener", 1.0f)); blendshapeMap.insert("EyeSquint_R", QPair("au_lid_tightener", 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("MouthLeft", QPair("au_lower_lip_x_push", 0.0f)); blendshapeMap.insert("LipsStretch_L", QPair("au_lip_stretcher_right", 1.0f)); blendshapeMap.insert("BrowsU_L", QPair("au_right_outer_brow_raiser", 1.0f)); blendshapeMap.insert("BrowsU_C", QPair("au_right_inner_brow_raiser", 0.5f)); 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 const float TRANSLATION_SCALE = 20.0f; void Visage::update() { #ifdef HAVE_VISAGE _active = (_tracker && _tracker->getTrackingData(_data) == TRACK_STAT_OK); if (!_active) { return; } _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]; #endif } void Visage::reset() { _headOrigin += _headTranslation / TRANSLATION_SCALE; }