diff --git a/domain-server/resources/web/footer.html b/domain-server/resources/web/footer.html
index d1a3fc29e8..aa6821a3b9 100644
--- a/domain-server/resources/web/footer.html
+++ b/domain-server/resources/web/footer.html
@@ -1,3 +1,4 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html
index 2be603b00e..a2aebc1189 100644
--- a/domain-server/resources/web/header.html
+++ b/domain-server/resources/web/header.html
@@ -8,4 +8,33 @@
+
\ No newline at end of file
diff --git a/domain-server/resources/web/js/domain-server.js b/domain-server/resources/web/js/domain-server.js
new file mode 100644
index 0000000000..3f78d8f466
--- /dev/null
+++ b/domain-server/resources/web/js/domain-server.js
@@ -0,0 +1,10 @@
+$(document).ready(function(){
+ var url = window.location;
+ // Will only work if string in href matches with location
+ $('ul.nav a[href="'+ url +'"]').parent().addClass('active');
+
+ // Will also work for relative and absolute hrefs
+ $('ul.nav a').filter(function() {
+ return this.href == url;
+ }).parent().addClass('active');
+});
\ No newline at end of file
diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json
index 2d90680e01..f4920a7b50 100644
--- a/domain-server/resources/web/settings/describe.json
+++ b/domain-server/resources/web/settings/describe.json
@@ -28,33 +28,5 @@
"default": ""
}
}
- },
- "voxels": {
- "label": "Voxels",
- "assignment-types": [2,3],
- "settings": {
- "voxel-wallet": {
- "label": "Destination Wallet ID",
- "help": "Wallet to be paid for voxel changes",
- "placeholder": "00000000-0000-0000-0000-000000000000",
- "default": ""
- },
- "per-voxel-credits": {
- "type": "double",
- "label": "Per Voxel Cost",
- "help": "Credit cost to change each voxel",
- "placeholder": "0.0",
- "default": "0.0",
- "input_addon": "₵"
- },
- "per-meter-cubed-credits": {
- "type": "double",
- "label": "Per Meter Cubed Cost",
- "help": "Credit cost to change each cubed meter of voxel space",
- "placeholder": "0.0",
- "default": "0.0",
- "input_addon": "₵"
- }
- }
}
}
\ No newline at end of file
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 2ecae73d0a..a76d3a916d 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -1280,6 +1280,9 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
const QByteArray HTTP_COOKIE_HEADER_KEY = "Cookie";
const QString ADMIN_USERS_CONFIG_KEY = "admin-users";
const QString ADMIN_ROLES_CONFIG_KEY = "admin-roles";
+ const QString BASIC_AUTH_CONFIG_KEY = "basic-auth";
+
+ const QByteArray UNAUTHENTICATED_BODY = "You do not have permission to access this domain-server.";
if (!_oauthProviderURL.isEmpty()
&& (_argumentVariantMap.contains(ADMIN_USERS_CONFIG_KEY) || _argumentVariantMap.contains(ADMIN_ROLES_CONFIG_KEY))) {
@@ -1293,6 +1296,11 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
cookieUUID = cookieUUIDRegex.cap(1);
}
+ if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) {
+ qDebug() << "Config file contains web admin settings for OAuth and basic HTTP authentication."
+ << "These cannot be combined - using OAuth for authentication.";
+ }
+
if (!cookieUUID.isNull() && _cookieSessionHash.contains(cookieUUID)) {
// pull the QJSONObject for the user with this cookie UUID
DomainServerWebSessionData sessionData = _cookieSessionHash.value(cookieUUID);
@@ -1315,8 +1323,7 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
}
}
- QString unauthenticatedRequest = "You do not have permission to access this domain-server.";
- connection->respond(HTTPConnection::StatusCode401, unauthenticatedRequest.toUtf8());
+ connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY);
// the user does not have allowed username or role, return 401
return false;
@@ -1340,6 +1347,59 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
// we don't know about this user yet, so they are not yet authenticated
return false;
}
+ } else if (_argumentVariantMap.contains(BASIC_AUTH_CONFIG_KEY)) {
+ // config file contains username and password combinations for basic auth
+ const QByteArray BASIC_AUTH_HEADER_KEY = "Authorization";
+
+ // check if a username and password have been provided with the request
+ QString basicAuthString = connection->requestHeaders().value(BASIC_AUTH_HEADER_KEY);
+
+ if (!basicAuthString.isEmpty()) {
+ QStringList splitAuthString = basicAuthString.split(' ');
+ QString base64String = splitAuthString.size() == 2 ? splitAuthString[1] : "";
+ QString credentialString = QByteArray::fromBase64(base64String.toLocal8Bit());
+
+ if (!credentialString.isEmpty()) {
+ QStringList credentialList = credentialString.split(':');
+ if (credentialList.size() == 2) {
+ QString username = credentialList[0];
+ QString password = credentialList[1];
+
+ // we've pulled a username and password - now check if there is a match in our basic auth hash
+ QJsonObject basicAuthObject = _argumentVariantMap.value(BASIC_AUTH_CONFIG_KEY).toJsonValue().toObject();
+
+ if (basicAuthObject.contains(username)) {
+ const QString BASIC_AUTH_USER_PASSWORD_KEY = "password";
+ QJsonObject userObject = basicAuthObject.value(username).toObject();
+
+ if (userObject.contains(BASIC_AUTH_USER_PASSWORD_KEY)
+ && userObject.value(BASIC_AUTH_USER_PASSWORD_KEY).toString() == password) {
+ // this is username / password match - let this user in
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ // basic HTTP auth being used but no username and password are present
+ // or the username and password are not correct
+ // send back a 401 and ask for basic auth
+
+ const QByteArray HTTP_AUTH_REQUEST_HEADER_KEY = "WWW-Authenticate";
+ static QString HTTP_AUTH_REALM_STRING = QString("Basic realm='%1 %2'")
+ .arg(_hostname.isEmpty() ? "localhost" : _hostname)
+ .arg("domain-server");
+
+ Headers basicAuthHeader;
+ basicAuthHeader.insert(HTTP_AUTH_REQUEST_HEADER_KEY, HTTP_AUTH_REALM_STRING.toUtf8());
+
+ connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY,
+ HTTPConnection::DefaultContentType, basicAuthHeader);
+
+ // not authenticated, bubble up false
+ return false;
+
} else {
// we don't have an OAuth URL + admin roles/usernames, so all users are authenticated
return true;
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 28640dc092..05a4a08c48 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1502,10 +1502,11 @@ glm::vec3 Application::getMouseVoxelWorldCoordinates(const VoxelDetail& mouseVox
}
FaceTracker* Application::getActiveFaceTracker() {
- return _cara.isActive() ? static_cast(&_cara) :
- (_faceshift.isActive() ? static_cast(&_faceshift) :
- (_faceplus.isActive() ? static_cast(&_faceplus) :
- (_visage.isActive() ? static_cast(&_visage) : NULL)));
+ return (_dde.isActive() ? static_cast(&_dde) :
+ (_cara.isActive() ? static_cast(&_cara) :
+ (_faceshift.isActive() ? static_cast(&_faceshift) :
+ (_faceplus.isActive() ? static_cast(&_faceplus) :
+ (_visage.isActive() ? static_cast(&_visage) : NULL)))));
}
struct SendVoxelsOperationArgs {
@@ -1937,13 +1938,21 @@ void Application::updateVisage() {
_visage.update();
}
+void Application::updateDDE() {
+ bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
+ PerformanceWarning warn(showWarnings, "Application::updateDDE()");
+
+ // Update Cara
+ _dde.update();
+}
+
void Application::updateCara() {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::updateCara()");
-
+
// Update Cara
_cara.update();
-
+
// Copy angular velocity if measured by cara, to the head
if (_cara.isActive()) {
_myAvatar->getHead()->setAngularVelocity(_cara.getHeadAngularVelocity());
@@ -3265,6 +3274,7 @@ void Application::resetSensors() {
_faceplus.reset();
_faceshift.reset();
_visage.reset();
+ _dde.reset();
OculusManager::reset();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 43a6083457..c898d10cbd 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -64,6 +64,7 @@
#include "devices/SixenseManager.h"
#include "devices/Visage.h"
#include "devices/CaraFaceTracker.h"
+#include "devices/DdeFaceTracker.h"
#include "models/ModelTreeRenderer.h"
#include "particles/ParticleTreeRenderer.h"
#include "renderer/AmbientOcclusionEffect.h"
@@ -214,6 +215,7 @@ public:
Faceplus* getFaceplus() { return &_faceplus; }
Faceshift* getFaceshift() { return &_faceshift; }
Visage* getVisage() { return &_visage; }
+ DdeFaceTracker* getDDE() { return &_dde; }
CaraFaceTracker* getCara() { return &_cara; }
FaceTracker* getActiveFaceTracker();
SixenseManager* getSixenseManager() { return &_sixenseManager; }
@@ -393,6 +395,7 @@ private:
void updateFaceplus();
void updateFaceshift();
void updateVisage();
+ void updateDDE();
void updateCara();
void updateMyAvatarLookAtPosition();
void updateThreads(float deltaTime);
@@ -493,6 +496,7 @@ private:
Faceshift _faceshift;
Visage _visage;
CaraFaceTracker _cara;
+ DdeFaceTracker _dde;
SixenseManager _sixenseManager;
PrioVR _prioVR;
diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp
index d3d1e74fc8..995f3f2390 100644
--- a/interface/src/avatar/Head.cpp
+++ b/interface/src/avatar/Head.cpp
@@ -68,6 +68,9 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) {
if ((_isFaceshiftConnected = faceTracker)) {
_blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
_isFaceshiftConnected = true;
+ } else if (Application::getInstance()->getDDE()->isActive()) {
+ faceTracker = Application::getInstance()->getDDE();
+ _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients();
}
}
diff --git a/interface/src/devices/CaraFaceTracker.h b/interface/src/devices/CaraFaceTracker.h
index 41dd205d22..0c715afda9 100644
--- a/interface/src/devices/CaraFaceTracker.h
+++ b/interface/src/devices/CaraFaceTracker.h
@@ -9,8 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-#ifndef hi_fi_CaraFaceTracker_h
-#define hi_fi_CaraFaceTracker_h
+#ifndef hifi_CaraFaceTracker_h
+#define hifi_CaraFaceTracker_h
#include
@@ -121,4 +121,4 @@ private:
int _jawOpenIndex;
};
-#endif //endif hi_fi_CaraFaceTracker_h
\ No newline at end of file
+#endif //endif hifi_CaraFaceTracker_h
diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp
new file mode 100644
index 0000000000..3dc5a818cc
--- /dev/null
+++ b/interface/src/devices/DdeFaceTracker.cpp
@@ -0,0 +1,245 @@
+//
+// 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 "DdeFaceTracker.h"
+
+static const QHostAddress DDE_FEATURE_POINT_SERVER_ADDR("127.0.0.1");
+static const quint16 DDE_FEATURE_POINT_SERVER_PORT = 5555;
+
+static const int NUM_EXPRESSION = 46;
+static const int MIN_PACKET_SIZE = (8 + NUM_EXPRESSION) * sizeof(float) + sizeof(int);
+static const int MAX_NAME_SIZE = 31;
+
+struct Packet{
+ //roughly in mm
+ float focal_length[1];
+ float translation[3];
+
+ //quaternion
+ float rotation[4];
+
+ //blendshape coefficients ranging between -0.2 and 1.5
+ float expressions[NUM_EXPRESSION];
+
+ //avatar id selected on the UI
+ int avatar_id;
+
+ //client name, arbitrary length
+ char name[MAX_NAME_SIZE + 1];
+};
+
+DdeFaceTracker::DdeFaceTracker() :
+_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)
+{
+ _blendshapeCoefficients.resize(NUM_EXPRESSION);
+
+ 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)));
+
+ bindTo(DDE_FEATURE_POINT_SERVER_PORT);
+}
+
+DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 port) :
+_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)
+{
+ _blendshapeCoefficients.resize(NUM_EXPRESSION);
+
+ connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
+ connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
+ connect(&_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
+
+ bindTo(host, port);
+}
+
+DdeFaceTracker::~DdeFaceTracker() {
+ if(_udpSocket.isOpen())
+ _udpSocket.close();
+}
+
+void DdeFaceTracker::init() {
+
+}
+
+void DdeFaceTracker::reset() {
+ _reset = true;
+}
+
+void DdeFaceTracker::update() {
+
+}
+
+void DdeFaceTracker::bindTo(quint16 port) {
+ bindTo(QHostAddress::Any, port);
+}
+
+void DdeFaceTracker::bindTo(const QHostAddress& host, quint16 port) {
+ if(_udpSocket.isOpen()) {
+ _udpSocket.close();
+ }
+ _udpSocket.bind(host, port);
+}
+
+bool DdeFaceTracker::isActive() const {
+ static const int ACTIVE_TIMEOUT_USECS = 3000000; //3 secs
+ return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS);
+}
+
+//private slots and methods
+void DdeFaceTracker::socketErrorOccurred(QAbstractSocket::SocketError socketError) {
+ qDebug() << "[Error] DDE Face Tracker Socket Error: " << _udpSocket.errorString();
+}
+
+void DdeFaceTracker::socketStateChanged(QAbstractSocket::SocketState socketState) {
+ QString state;
+ switch(socketState) {
+ case QAbstractSocket::BoundState:
+ state = "Bounded";
+ 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;
+ }
+ qDebug() << "[Info] DDE Face Tracker Socket: " << socketState;
+}
+
+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;
+}
+
+static const float DDE_MIN_RANGE = -0.2;
+static const float DDE_MAX_RANGE = 1.5;
+float rescaleCoef(float ddeCoef) {
+ return (ddeCoef - DDE_MIN_RANGE) / (DDE_MAX_RANGE - DDE_MIN_RANGE);
+}
+
+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) {
+ memcpy(&_referenceTranslation, &translation, sizeof(glm::vec3));
+ memcpy(&_referenceRotation, &rotation, sizeof(glm::quat));
+ _reset = false;
+ }
+
+ // Compute relative translation
+ float LEAN_DAMPING_FACTOR = 40;
+ translation -= _referenceTranslation;
+ translation /= LEAN_DAMPING_FACTOR;
+ translation.x *= -1;
+
+ // Compute relative rotation
+ rotation = glm::inverse(_referenceRotation) * rotation;
+
+ // copy values
+ _headTranslation = translation;
+ _headRotation = rotation;
+
+ // Set blendshapes
+ float BLINK_MAGNIFIER = 2.0f;
+ _blendshapeCoefficients[_leftBlinkIndex] = rescaleCoef(packet.expressions[1]) * BLINK_MAGNIFIER;
+ _blendshapeCoefficients[_rightBlinkIndex] = rescaleCoef(packet.expressions[0]) * BLINK_MAGNIFIER;
+
+ float leftBrow = 1.0f - rescaleCoef(packet.expressions[14]);
+ if (leftBrow < 0.5f) {
+ _blendshapeCoefficients[_browDownLeftIndex] = 1.0f - 2.0f * leftBrow;
+ _blendshapeCoefficients[_browUpLeftIndex] = 0.0f;
+ } else {
+ _blendshapeCoefficients[_browDownLeftIndex] = 0.0f;
+ _blendshapeCoefficients[_browUpLeftIndex] = 2.0f * (leftBrow - 0.5f);
+ }
+ float rightBrow = 1.0f - rescaleCoef(packet.expressions[15]);
+ if (rightBrow < 0.5f) {
+ _blendshapeCoefficients[_browDownRightIndex] = 1.0f - 2.0f * rightBrow;
+ _blendshapeCoefficients[_browUpRightIndex] = 0.0f;
+ } else {
+ _blendshapeCoefficients[_browDownRightIndex] = 0.0f;
+ _blendshapeCoefficients[_browUpRightIndex] = 2.0f * (rightBrow - 0.5f);
+ }
+
+ float JAW_OPEN_MAGNIFIER = 1.4f;
+ _blendshapeCoefficients[_jawOpenIndex] = rescaleCoef(packet.expressions[21]) * JAW_OPEN_MAGNIFIER;
+
+
+ _blendshapeCoefficients[_mouthSmileLeftIndex] = rescaleCoef(packet.expressions[24]);
+ _blendshapeCoefficients[_mouthSmileRightIndex] = rescaleCoef(packet.expressions[23]);
+
+
+ } else {
+ qDebug() << "[Error] DDE Face Tracker Decode Error";
+ }
+ _lastReceiveTimestamp = usecTimestampNow();
+}
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
new file mode 100644
index 0000000000..96707ac94c
--- /dev/null
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -0,0 +1,89 @@
+//
+// DdeFaceTracker.h
+//
+//
+// 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
+//
+
+#ifndef hifi_DdeFaceTracker_h
+#define hifi_DdeFaceTracker_h
+
+#include
+
+#include "FaceTracker.h"
+
+class DdeFaceTracker : public FaceTracker {
+ Q_OBJECT
+
+public:
+ DdeFaceTracker();
+ DdeFaceTracker(const QHostAddress& host, quint16 port);
+ ~DdeFaceTracker();
+
+ //initialization
+ void init();
+ void reset();
+ void update();
+
+ //sockets
+ void bindTo(quint16 port);
+ void bindTo(const QHostAddress& host, quint16 port);
+ bool isActive() const;
+
+ float getLeftBlink() const { return getBlendshapeCoefficient(_leftBlinkIndex); }
+ float getRightBlink() const { return getBlendshapeCoefficient(_rightBlinkIndex); }
+ float getLeftEyeOpen() const { return getBlendshapeCoefficient(_leftEyeOpenIndex); }
+ float getRightEyeOpen() const { return getBlendshapeCoefficient(_rightEyeOpenIndex); }
+
+ float getBrowDownLeft() const { return getBlendshapeCoefficient(_browDownLeftIndex); }
+ float getBrowDownRight() const { return getBlendshapeCoefficient(_browDownRightIndex); }
+ float getBrowUpCenter() const { return getBlendshapeCoefficient(_browUpCenterIndex); }
+ float getBrowUpLeft() const { return getBlendshapeCoefficient(_browUpLeftIndex); }
+ float getBrowUpRight() const { return getBlendshapeCoefficient(_browUpRightIndex); }
+
+ float getMouthSize() const { return getBlendshapeCoefficient(_jawOpenIndex); }
+ float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
+ float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
+
+private slots:
+
+ //sockets
+ void socketErrorOccurred(QAbstractSocket::SocketError socketError);
+ void readPendingDatagrams();
+ void socketStateChanged(QAbstractSocket::SocketState socketState);
+
+private:
+ float getBlendshapeCoefficient(int index) const;
+ void decodePacket(const QByteArray& buffer);
+
+ // sockets
+ QUdpSocket _udpSocket;
+ quint64 _lastReceiveTimestamp;
+
+ bool _reset;
+ glm::vec3 _referenceTranslation;
+ glm::quat _referenceRotation;
+
+ int _leftBlinkIndex;
+ int _rightBlinkIndex;
+ int _leftEyeOpenIndex;
+ int _rightEyeOpenIndex;
+
+ // Brows
+ int _browDownLeftIndex;
+ int _browDownRightIndex;
+ int _browUpCenterIndex;
+ int _browUpLeftIndex;
+ int _browUpRightIndex;
+
+ int _mouthSmileLeftIndex;
+ int _mouthSmileRightIndex;
+
+ int _jawOpenIndex;
+};
+
+#endif // hifi_DdeFaceTracker_h
\ No newline at end of file
diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp
index 02cce80104..6fe39a8ccf 100644
--- a/libraries/shared/src/HifiConfigVariantMap.cpp
+++ b/libraries/shared/src/HifiConfigVariantMap.cpp
@@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
#include
#include
#include
@@ -68,29 +69,35 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
+ QString configFilePath;
+
if (configIndex != -1) {
// we have a config file - try and read it
- QString configFilePath = argumentList[configIndex + 1];
- QFile configFile(configFilePath);
-
- if (configFile.exists()) {
- qDebug() << "Reading JSON config file at" << configFilePath;
- configFile.open(QIODevice::ReadOnly);
-
- QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
- QJsonObject rootObject = configDocument.object();
-
- // enumerate the keys of the configDocument object
- foreach(const QString& key, rootObject.keys()) {
-
- if (!mergedMap.contains(key)) {
- // no match in existing list, add it
- mergedMap.insert(key, QVariant(rootObject[key]));
- }
+ configFilePath = argumentList[configIndex + 1];
+ } else {
+ // no config file - try to read a file at resources/config.json
+ configFilePath = QCoreApplication::applicationDirPath() + "/resources/config.json";
+ }
+
+ QFile configFile(configFilePath);
+
+ if (configFile.exists()) {
+ qDebug() << "Reading JSON config file at" << configFilePath;
+ configFile.open(QIODevice::ReadOnly);
+
+ QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
+ QJsonObject rootObject = configDocument.object();
+
+ // enumerate the keys of the configDocument object
+ foreach(const QString& key, rootObject.keys()) {
+
+ if (!mergedMap.contains(key)) {
+ // no match in existing list, add it
+ mergedMap.insert(key, QVariant(rootObject[key]));
}
- } else {
- qDebug() << "Could not find JSON config file at" << configFilePath;
}
+ } else {
+ qDebug() << "Could not find JSON config file at" << configFilePath;
}
return mergedMap;