// // DdeFaceTracker.cpp // // // Created by Clement on 8/2/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 "DdeFaceTracker.h" #include "FaceshiftConstants.h" #include "InterfaceLogging.h" #include "Menu.h" static const QHostAddress DDE_SERVER_ADDR("127.0.0.1"); static const quint16 DDE_SERVER_PORT = 64204; static const quint16 DDE_CONTROL_PORT = 64205; #if defined(Q_OS_WIN) static const QString DDE_PROGRAM_PATH = QCoreApplication::applicationDirPath() + "/dde/dde.exe"; #elif defined(Q_OS_MAC) static const QString DDE_PROGRAM_PATH = QCoreApplication::applicationDirPath() + "/dde.app/Contents/MacOS/dde"; #endif static const QStringList DDE_ARGUMENTS = QStringList() << "--udp=" + DDE_SERVER_ADDR.toString() + ":" + QString::number(DDE_SERVER_PORT) << "--receiver=" + QString::number(DDE_CONTROL_PORT) << "--headless"; static const int NUM_EXPRESSIONS = 46; static const int MIN_PACKET_SIZE = (8 + NUM_EXPRESSIONS) * sizeof(float) + sizeof(int); static const int MAX_NAME_SIZE = 31; // There's almost but not quite a 1-1 correspondence between DDE's 46 and Faceshift 1.3's 48 packets. // The best guess at mapping is to: // - Swap L and R values // - Skip two Faceshift values: JawChew (22) and LipsLowerDown (37) static const int DDE_TO_FACESHIFT_MAPPING[] = { 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 16, 18, 17, 19, 23, 21, // Skip JawChew 20, 25, 24, 27, 26, 29, 28, 31, 30, 33, 32, 34, 35, 36, // Skip LipsLowerDown 38, 39, 40, 41, 42, 43, 44, 45, 47, 46 }; // The DDE coefficients, overall, range from -0.2 to 1.5 or so. However, individual coefficients typically vary much // less than this. static const float DDE_COEFFICIENT_SCALES[] = { 4.0f, // EyeBlink_L 4.0f, // EyeBlink_R 1.0f, // EyeSquint_L 1.0f, // EyeSquint_R 1.0f, // EyeDown_L 1.0f, // EyeDown_R 1.0f, // EyeIn_L 1.0f, // EyeIn_R 4.0f, // EyeOpen_L 4.0f, // EyeOpen_R 1.0f, // EyeOut_L 1.0f, // EyeOut_R 1.0f, // EyeUp_L 1.0f, // EyeUp_R 3.0f, // BrowsD_L 3.0f, // BrowsD_R 3.0f, // BrowsU_C 3.0f, // BrowsU_L 3.0f, // BrowsU_R 1.0f, // JawFwd 1.5f, // JawLeft 1.8f, // JawOpen 1.0f, // JawChew 1.5f, // JawRight 1.5f, // MouthLeft 1.5f, // MouthRight 1.5f, // MouthFrown_L 1.5f, // MouthFrown_R 1.5f, // MouthSmile_L 1.5f, // MouthSmile_R 1.0f, // MouthDimple_L 1.0f, // MouthDimple_R 1.0f, // LipsStretch_L 1.0f, // LipsStretch_R 1.0f, // LipsUpperClose 1.0f, // LipsLowerClose 1.0f, // LipsUpperUp 1.0f, // LipsLowerDown 1.0f, // LipsUpperOpen 1.0f, // LipsLowerOpen 2.5f, // LipsFunnel 2.0f, // LipsPucker 1.5f, // ChinLowerRaise 1.5f, // ChinUpperRaise 1.0f, // Sneer 3.0f, // Puff 1.0f, // CheekSquint_L 1.0f // CheekSquint_R }; struct Packet { //roughly in mm float focal_length[1]; float translation[3]; //quaternion float rotation[4]; // The DDE coefficients, overall, range from -0.2 to 1.5 or so. However, individual coefficients typically vary much // less than this. float expressions[NUM_EXPRESSIONS]; //avatar id selected on the UI int avatar_id; //client name, arbitrary length char name[MAX_NAME_SIZE + 1]; }; DdeFaceTracker::DdeFaceTracker() : DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT) { } DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, quint16 controlPort) : _ddeProcess(NULL), _host(host), _serverPort(serverPort), _controlPort(controlPort), _lastReceiveTimestamp(0), _reset(false), _leftBlinkIndex(0), // see http://support.faceshift.com/support/articles/35129-export-of-blendshapes _rightBlinkIndex(1), _leftEyeOpenIndex(8), _rightEyeOpenIndex(9), _browDownLeftIndex(14), _browDownRightIndex(15), _browUpCenterIndex(16), _browUpLeftIndex(17), _browUpRightIndex(18), _mouthSmileLeftIndex(28), _mouthSmileRightIndex(29), _jawOpenIndex(21), _previousTranslation(glm::vec3()), _previousRotation(glm::quat()) { _coefficients.resize(NUM_FACESHIFT_BLENDSHAPES); _previousCoefficients.resize(NUM_FACESHIFT_BLENDSHAPES); _blendshapeCoefficients.resize(NUM_FACESHIFT_BLENDSHAPES); connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState))); } DdeFaceTracker::~DdeFaceTracker() { setEnabled(false); } void DdeFaceTracker::setEnabled(bool enabled) { #ifdef HAVE_DDE // isOpen() does not work as one might expect on QUdpSocket; don't test isOpen() before closing socket. _udpSocket.close(); if (enabled) { _udpSocket.bind(_host, _serverPort); } const char* DDE_EXIT_COMMAND = "exit"; if (enabled && !_ddeProcess) { // Terminate any existing DDE process, perhaps left running after an Interface crash _udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort); qDebug() << "[Info] DDE Face Tracker Starting"; _ddeProcess = new QProcess(qApp); connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus))); _ddeProcess->start(QCoreApplication::applicationDirPath() + DDE_PROGRAM_PATH, DDE_ARGUMENTS); } if (!enabled && _ddeProcess) { _udpSocket.writeDatagram(DDE_EXIT_COMMAND, DDE_SERVER_ADDR, _controlPort); delete _ddeProcess; _ddeProcess = NULL; qDebug() << "[Info] DDE Face Tracker Stopped"; } #endif } void DdeFaceTracker::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (_ddeProcess) { // DDE crashed or was manually terminated qDebug() << "[Info] DDE Face Tracker Stopped Unexpectedly"; _udpSocket.close(); _ddeProcess = NULL; Menu::getInstance()->setIsOptionChecked(MenuOption::NoFaceTracking, true); } } void DdeFaceTracker::resetTracking() { qDebug() << "[Info] Reset DDE Tracking"; const char* DDE_RESET_COMMAND = "reset"; _udpSocket.writeDatagram(DDE_RESET_COMMAND, DDE_SERVER_ADDR, _controlPort); } bool DdeFaceTracker::isActive() const { static const quint64 ACTIVE_TIMEOUT_USECS = 3000000; //3 secs return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS); } //private slots and methods void DdeFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) { qCDebug(interfaceapp) << "[Error] DDE Face Tracker Socket Error: " << _udpSocket.errorString(); } void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) { QString state; switch(socketState) { case QAbstractSocket::BoundState: state = "Bound"; break; case QAbstractSocket::ClosingState: state = "Closing"; break; case QAbstractSocket::ConnectedState: state = "Connected"; break; case QAbstractSocket::ConnectingState: state = "Connecting"; break; case QAbstractSocket::HostLookupState: state = "Host Lookup"; break; case QAbstractSocket::ListeningState: state = "Listening"; break; case QAbstractSocket::UnconnectedState: state = "Unconnected"; break; } qCDebug(interfaceapp) << "[Info] DDE Face Tracker Socket: " << state; } void DdeFaceTracker::readPendingDatagrams() { QByteArray buffer; while (_udpSocket.hasPendingDatagrams()) { buffer.resize(_udpSocket.pendingDatagramSize()); _udpSocket.readDatagram(buffer.data(), buffer.size()); } decodePacket(buffer); } float DdeFaceTracker::getBlendshapeCoefficient(int index) const { return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f; } void DdeFaceTracker::decodePacket(const QByteArray& buffer) { if(buffer.size() > MIN_PACKET_SIZE) { Packet packet; int bytesToCopy = glm::min((int)sizeof(packet), buffer.size()); memset(&packet.name, '\n', MAX_NAME_SIZE + 1); memcpy(&packet, buffer.data(), bytesToCopy); glm::vec3 translation; memcpy(&translation, packet.translation, sizeof(packet.translation)); glm::quat rotation; memcpy(&rotation, &packet.rotation, sizeof(packet.rotation)); if (_reset || (_lastReceiveTimestamp == 0)) { memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3)); memcpy(&_referenceRotation, &rotation, sizeof(glm::quat)); _reset = false; } // Compute relative translation float LEAN_DAMPING_FACTOR = 75.0f; translation -= _referenceTranslation; translation /= LEAN_DAMPING_FACTOR; translation.x *= -1; _headTranslation = (translation + _previousTranslation) / 2.0f; _previousTranslation = translation; // Compute relative rotation rotation = glm::inverse(_referenceRotation) * rotation; _headRotation = (rotation + _previousRotation) / 2.0f; _previousRotation = rotation; // Translate DDE coefficients to Faceshift compatible coefficients for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { _coefficients[DDE_TO_FACESHIFT_MAPPING[i]] = packet.expressions[i]; } // Use EyeBlink values to control both EyeBlink and EyeOpen static const float RELAXED_EYE_VALUE = 0.1f; float leftEye = (_coefficients[_leftBlinkIndex] + _previousCoefficients[_leftBlinkIndex]) / 2.0f; float rightEye = (_coefficients[_rightBlinkIndex] + _previousCoefficients[_rightBlinkIndex]) / 2.0f; if (leftEye > RELAXED_EYE_VALUE) { _coefficients[_leftBlinkIndex] = leftEye - RELAXED_EYE_VALUE; _coefficients[_leftEyeOpenIndex] = 0.0f; } else { _coefficients[_leftBlinkIndex] = 0.0f; _coefficients[_leftEyeOpenIndex] = RELAXED_EYE_VALUE - leftEye; } if (rightEye > RELAXED_EYE_VALUE) { _coefficients[_rightBlinkIndex] = rightEye - RELAXED_EYE_VALUE; _coefficients[_rightEyeOpenIndex] = 0.0f; } else { _coefficients[_rightBlinkIndex] = 0.0f; _coefficients[_rightEyeOpenIndex] = RELAXED_EYE_VALUE - rightEye; } // Use BrowsU_C to control both brows' up and down _coefficients[_browDownLeftIndex] = -_coefficients[_browUpCenterIndex]; _coefficients[_browDownRightIndex] = -_coefficients[_browUpCenterIndex]; _coefficients[_browUpLeftIndex] = _coefficients[_browUpCenterIndex]; _coefficients[_browUpRightIndex] = _coefficients[_browUpCenterIndex]; // Offset jaw open coefficient static const float JAW_OPEN_THRESHOLD = 0.16f; _coefficients[_jawOpenIndex] = _coefficients[_jawOpenIndex] - JAW_OPEN_THRESHOLD; // Offset smile coefficients static const float SMILE_THRESHOLD = 0.18f; _coefficients[_mouthSmileLeftIndex] = _coefficients[_mouthSmileLeftIndex] - SMILE_THRESHOLD; _coefficients[_mouthSmileRightIndex] = _coefficients[_mouthSmileRightIndex] - SMILE_THRESHOLD; // Scale all coefficients for (int i = 0; i < NUM_EXPRESSIONS; i += 1) { _blendshapeCoefficients[i] = glm::clamp(DDE_COEFFICIENT_SCALES[i] * (_coefficients[i] + _previousCoefficients[i]) / 2.0f, 0.0f, 1.0f); _previousCoefficients[i] = _coefficients[i]; } } else { qCDebug(interfaceapp) << "[Error] DDE Face Tracker Decode Error"; } _lastReceiveTimestamp = usecTimestampNow(); }